是否可以在 C# 中实现 mixin?
我听说可以使用扩展方法,但我自己不太明白。 如果可能的话我想看一个具体的例子。
谢谢!
I've heard that it's possible with extension methods, but I can't quite figure it out myself. I'd like to see a specific example if possible.
Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
这实际上取决于“mixin”的含义 - 每个人似乎都有稍微不同的想法。 我希望看到的混合类型(但在 C# 中不可用)使组合实现变得简单:
编译器只需将每个成员代理为“impl”即可实现 ISomeInterface除非类中直接有另一个实现。
不过目前这一切都是不可能的:)
It really depends on what you mean by "mixin" - everyone seems to have a slightly different idea. The kind of mixin I'd like to see (but which isn't available in C#) is making implementation-through-composition simple:
The compiler would implement ISomeInterface just by proxying every member to "impl" unless there was another implementation in the class directly.
None of this is possible at the moment though :)
我通常采用这种模式:
我在同一个源文件/命名空间中有两个定义。
这样,当使用接口(使用“using”)时,扩展始终可用。
这为您提供了有限的混合,如 CMS 的第一个链接中所述。
限制:
对于许多情况来说这仍然足够了。
如果他们(MS)可以添加一些编译器魔法来自动生成扩展类,那就太好了:
尽管 Jon 提出的编译器技巧会更好。
I usually employ this pattern:
I have the two definitions in the same source file/namespace.
That way the extensions are always available when the interface is used (with 'using').
This gives you a limited mixin as described in CMS' first link.
Limitations:
It's still sufficient for many situations.
It would be nice if they (MS) could add some compiler magic to auto-generate the extension class:
Although Jon's proposed compiler trick would be even nicer.
有一个开源框架可让您通过 C# 实现 mixin。 看看 http://remix.codeplex.com/。
使用这个框架很容易实现 mixin。 只需查看示例和页面上给出的“其他信息”链接即可。
There is an open source framework that enables you to implement mixins via C#. Have a look on http://remix.codeplex.com/.
It is very easy to implement mixins with this framework. Just have a look on the samples and the "Additional Information" links given on the page.
LinFu 和 Castle 的 DynamicProxy 实现 mixins。 COP(面向复合编程)可以被认为是从 mixins 中构建出一个完整的范式。 Anders Noras 的这篇文章 包含有用的信息和链接。
编辑:这在 C# 2.0 中都是可能的,无需扩展方法
LinFu and Castle's DynamicProxy implement mixins. COP (Composite Oriented Programming) could be considered as making a whole paradigm out of mixins. This post from Anders Noras has useful informations and links.
EDIT: This is all possible with C# 2.0, without extension methods
我需要类似的东西,所以我使用 Reflection.Emit 想出了以下内容。 在下面的代码中,动态生成一个新类型,它具有类型“mixin”的私有成员。 所有对“mixin”接口方法的调用都转发给这个私有成员。 定义了一个单参数构造函数,它采用一个实现“mixin”接口的实例。 基本上,它等于为给定的具体类型 T 和接口 I 编写以下代码:
这是类:
这是用法:
I needed something similar so I came up with the following using Reflection.Emit. In the following code a new type is dynamically generated which has a private member of type 'mixin'. All the calls to methods of 'mixin' interface are forwarded to this private member. A single parameter constructor is defined that takes an instance which implements the 'mixin' interface. Basically, it is equal to writing the following code for a given concrete type T and interface I:
This is the class:
This is the usage:
您还可以增强扩展方法来合并状态,其模式与 WPF 的附加属性不同。
这是一个具有最少样板的示例。 请注意,不需要对目标类进行任何修改,包括添加接口,除非您需要以多态方式处理目标类 - 在这种情况下,您最终会得到非常接近实际多重继承的东西。
用法:
请注意,它也适用于 null 实例,因为扩展方法自然也适用。
您还可以考虑使用 WeakDictionary 实现来避免由于集合将目标类引用作为键而导致内存泄漏。
You could also augment the extension method approach to incorporate state, in a pattern not unlike WPF's attached properties.
Here is an example with minimum boilerplate. Note that no modification are required on the target classes, including adding interfaces, unless you need to deal with the target class polymorphically - in which case you end up with something very close to actual Multiple Inheritance.
Usage:
Note that it also works with null instances, since extension methods naturally do.
You might also consider using a WeakDictionary implementation to avoid memory leaks caused by the collection's holding on to target class references as keys.
我找到了解决方法 这里,虽然不完全优雅,但允许您实现完全可观察的 mixin 行为。 此外,IntelliSense 仍然有效!
I've found a workaround here, which while not entirely elegant, allows you to achieve fully observable mixin behavior. Additionally, IntelliSense still works!
如果您有一个可以存储数据的基类,您可以强制编译器安全并使用标记接口。
这或多或少是已接受答案中的“C# 3.0 中的 Mixins”所建议的。
ObjectBase:
因此,如果您有一个类,您可以从“ObjectBase”继承并使用 IHasStuff 进行装饰,您现在可以添加 sutff
If you have a base class that can store data you can enforce compiler safety and use marker interfaces.
That's more or less what "Mixins in C# 3.0" from the accepted answer proposes.
The ObjectBase:
So if you have a Class you can inherit from 'ObjectBase' and decorate with IHasStuff you can add sutff now
这是我刚刚想出的一个 mixin 实现。 我可能会将它与我的库一起使用。
这可能是以前在某个地方做过的。
都是静态类型的,没有字典什么的。 每个类型都需要一点额外的代码,每个实例不需要任何存储。 另一方面,如果您愿意,它还为您提供了动态更改 mixin 实现的灵活性。 没有构建后、构建前、构建中期工具。
它有一些限制,但它确实允许诸如覆盖之类的事情。
我们首先定义一个标记接口。 也许稍后会添加一些东西:
这个接口是通过 mixins 实现的。 Mixin 是常规类。 类型不直接继承或实现 mixin。 相反,它们只是使用接口公开 mixin 的实例:
实现此接口意味着支持 mixin。 明确地实现它很重要,因为每种类型我们都会有几个这样的。
现在介绍一下使用扩展方法的小技巧。 我们定义:
Mixout
公开适当类型的 mixin。 现在,为了测试这一点,让我们定义:相当有趣的是(尽管回想起来,它确实有意义),IntelliSense 没有检测到扩展方法
Mixout
适用于Test
,但只要Test
实际上有 mixin,编译器就会接受它。 如果你尝试,它会给你一个编译错误。
您也可以花点心思,也定义以下方法:
它的作用是,a) 在 IntelliSense 中显示一个名为
Mixout
的方法,提醒您它的存在,b) 提供更具描述性的方法错误消息(由Obsolete
属性生成)。Here is a mixin implementation I've just come up with. I'll probably use it with a library of mine.
It's probably been done before, somewhere.
It's all statically typed, with no dictionaries or something. It requires a little bit of extra code per type, you don't need any storage per instance. On the other hand, it also gives you the flexibility of changing the mixin implementation on the fly, if you so desire. No post-build, pre-build, mid-build tools.
It has some limitations, but it does allow things like overriding and so on.
We begin by defining a marker interface. Perhaps something will be added to it later:
This interface is implemented by mixins. Mixins are regular classes. Types do not inherit or implement mixins directly. They instead just expose an instance of the mixin using the interface:
Implementing this interface means supporting the mixin. It's important that it's implemented explicitly, since we're going to have several of these per type.
Now for a little trick using extension methods. We define:
Mixout
exposes the mixin of the appropriate type. Now, to test this out, let's define:Rather amusingly (though in retrospect, it does make sense), IntelliSense does not detect that the extension method
Mixout
applies toTest
, but the compiler does accept it, as long asTest
actually has the mixin. If you try,It gives you a compilation error.
You can go a bit fancy, and define the following method too:
What this does is, a) display a method called
Mixout
in IntelliSense, reminding you of its existence, and b) provide a somewhat more descriptive error message (generated by theObsolete
attribute).从根本上讲,有几种技术可以在类中获取 Mixin 行为:
using
和foreach
之类的结构。ConditionalWeakTable
来保存数据来非常粗略地实现。There are, fundamentally, several techniques to getting Mixin behavior in your classes:
IEnumerable
andIDisposable
) can use default interface members with explicit implementation of said interface. Then, then new interface behaves as a mixin where concrete behavior can be added and leveraged without using extension methods, and can support constructs such asusing
andforeach
.ConditionalWeakTable
to hold data.接口的默认实现几乎使混入成为可能,只要有一点创造力。
这是一个演示:
因此,这是通过将 mixin 定义为
struct
和相应的interface
来实现的; 其中接口包含 mixin 默认实现,而 struct 包含状态。 此实现的优点是,当您向 mixin 添加状态或方法时,不会破坏 ABI。唯一真正的缺点是无法进行
protected
访问限定符。 您唯一的保护选择是public
和private
。 您可以通过不提供转发器来进行假保护,但它实际上不起作用,因为一半的调用者将通过接口类型的变量,它可以看到 Impl 成员并访问结构体的成员。这是隔离的 mixin 用户的开销,这样你就可以单独看到它:
就是这样。 一个接口声明和两行代码。 第一行声明并创建 mixin 状态,第二行提供将所有内容连接在一起的粘合剂。 您可以拥有的 mixin 数量也没有限制。
mixin 提供程序的开销如下:
访问任何
private
内容的每个public
属性和方法都需要一个从interface
到的转发器>结构。
以这种方式完成的 Mixins 没有继承; 但是,您可以通过组合来伪造它并在派生的
struct
中实现基本接口。接口
转发提供了执行虚拟方法的能力。 这也意味着 mixins 可以有 mixins,但是如果你需要的话,你在做什么。道歉; TIO 的编译器已过时且不支持默认实现。
Default implementation of interfaces have pretty much enabled mixins to be possible with a bit of creativity.
Here's a demo:
So this works by defining a mixin as a
struct
and a correspondinginterface
; where the interface contains the mixin default implementation and thestruct
contains the state. This implementation has the benefit of not breaking the ABI when you add either state or methods to the mixin.The only real downside is there's no way to do
protected
access qualifier. Your only protection choices arepublic
andprivate
. You can kind of fake protected by not providing the forwarder but it doesn't really work because half of the callers will be via variables of the interface type, which can see the Impl member and access members of the struct.Here's the overhead in the user of the mixin isolated so you can see it alone:
That's it. One interface declaration and two lines of code. The first line declares and creates the mixin state and the second line provides the glue that ties everything together. There's no limit to the number of mixins you can have either.
The overhead on the mixin provider is as follows:
Every
public
property and method that accesses anythingprivate
needs a forwarder from theinterface
to thestruct
.Mixins done this way don't have inheritance; however you can fake it with composition and implementing the base interface in the derived
struct
. Theinterface
forwarding provides capacity to do virtual methods while you're at it. This also means mixins can have mixins, but what are you doing if you need that.Apologies; TIO's compiler is out of date and doesn't support default implementations.