为什么是委托参考类型? [英] Why are delegates reference types?

查看:179
本文介绍了为什么是委托参考类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对接受的答案的快速说明:我不同意 Jeffrey的回答,即由于 Delegate 必须是一个引用类型,所以所有的代理都是引用类型。 (根本不是一个多级继承链排除值类型;所有枚举类型,例如,继承自 System.Enum ,它们依次继承自 System.ValueType ,它继承自 System.Object 所有引用类型)。我认为,从根本上说,所有代表事实上不仅仅是从委托而是从 MulticastDelegate 继承是关键的实现这里。作为 Raymond指出在评论中他的答案,一旦你承诺支持多个订阅者,由于需要一个阵列,所以不是使用引用类型代替本身,真的没有意义。

Quick note on the accepted answer: I disagree with a small part of Jeffrey's answer, namely the point that since Delegate had to be a reference type, it follows that all delegates are reference types. (It simply isn't true that a multi-level inheritance chain rules out value types; all enum types, for example, inherit from System.Enum, which in turn inherits from System.ValueType, which inherits from System.Object, all reference types.) However I think the fact that, fundamentally, all delegates in fact inherit not just from Delegate but from MulticastDelegate is the critical realization here. As Raymond points out in a comment to his answer, once you've committed to supporting multiple subscribers, there's really no point in not using a reference type for the delegate itself, given the need for an array somewhere.

请参阅底部的更新。

对我来说似乎很奇怪,如果我这样做:

It has always seemed strange to me that if I do this:

Action foo = obj.Foo;

我正在创建一个新的 Action 对象,每次。我确定成本是最小的,但它涉及到分配内存,以便以后被垃圾回收。

I am creating a new Action object, every time. I'm sure the cost is minimal, but it involves allocation of memory to later be garbage collected.

鉴于代理人本身就是不可变的我不知道为什么他们不能是价值类型?那么一行代码就像上面这样一个代码,只不过是把一个简单的赋值给堆栈上的一个内存地址*。

Given that delegates are inherently themselves immutable, I wonder why they couldn't be value types? Then a line of code like the one above would incur nothing more than a simple assignment to a memory address on the stack*.

即使考虑匿名函数,似乎)这将工作。请考虑以下简单示例。

Even considering anonymous functions, it seems (to me) this would work. Consider the following simple example.

Action foo = () => { obj.Foo(); };

在这种情况下, foo 关闭,是的。在许多情况下,我想象这样做需要一个实际的引用类型(例如当局部变量被关闭并在闭包中被修改时)。 但在某些情况下,不应该。例如在上述情况下,似乎支持关闭的类型可能如下所示: 我回覆了我关于此的原始点。以下确实需要是一个引用类型(或者:它不需要是,但如果它是一个 struct ,它只是去无论如何得到盒装)。所以,忽略以下代码示例。我只是为了提供具体提到的答案的上下文。

In this case foo does constitute a closure, yes. And in many cases, I imagine this does require an actual reference type (such as when local variables are closed over and are modified within the closure). But in some cases, it shouldn't. For instance in the above case, it seems that a type to support the closure could look like this: I take back my original point about this. The below really does need to be a reference type (or: it doesn't need to be, but if it's a struct it's just going to get boxed anyway). So, disregard the below code example. I leave it only to provide context for answers the specfically mention it.

struct CompilerGenerated
{
    Obj obj;

    public CompilerGenerated(Obj obj)
    {
        this.obj = obj;
    }

    public void CallFoo()
    {
        obj.Foo();
    }
}

// ...elsewhere...

// This would not require any long-term memory allocation
// if Action were a value type, since CompilerGenerated
// is also a value type.
Action foo = new CompilerGenerated(obj).CallFoo;

这个问题是否有意义?正如我所看到的,有两种可能的解释:

Does this question make sense? As I see it, there are two possible explanations:


  • 正如实施代理一样,价值类型将需要额外的工作/复杂性,因为支持 修改局部变量的值的事情将会需要编译器生成的引用类型。

  • 其他其他原因为什么代理人简单地将不能实现为价值类型。

  • Implementing delegates properly as value types would have required additional work/complexity, since support for things like closures that do modify values of local variables would have required compiler-generated reference types anyway.
  • There are some other reasons why, under the hood, delegates simply can't be implemented as value types.

最后我不会因此而失去任何睡眠这只是我一直好奇的一段时间。

In the end, I'm not losing any sleep over this; it's just something I've been curious about for a little while.

更新:在对于Ani的评论的回应,我看到为什么我上面的例子中的 CompilerGenerated 类型也可能是一个引用类型,因为如果一个委托将要组成一个函数指针和一个对象指针,它将需要一个引用类型(至少对于使用闭包的匿名函数,因为即使你引入了一个额外的泛型类型参数 - 例如, Action< TCaller> 不会覆盖不能命名的类型!)。 然而,所有这一切都使我感到遗憾的是,将编译器生成的类型的问题引入讨论!我的主要问题是关于代表,即与函数指针和对象指针的东西。在我看来, 可能是一个值类型。

Update: In response to Ani's comment, I see why the CompilerGenerated type in my above example might as well be a reference type, since if a delegate is going to comprise a function pointer and an object pointer it'll need a reference type anyway (at least for anonymous functions using closures, since even if you introduced an additional generic type parameter—e.g., Action<TCaller>—this wouldn't cover types that can't be named!). However, all this does is kind of make me regret bringing the question of compiler-generated types for closures into the discussion at all! My main question is about delegates, i.e., the thing with the function pointer and the object pointer. It still seems to me that could be a value type.

换句话说,即使这样...

In other words, even if this...

Action foo = () => { obj.Foo(); };

...需要创建一个引用类型对象(以支持关闭,并给代表一些参考),为什么需要创建两个(闭包支持对象 Action delegate)?

...requires the creation of one reference type object (to support the closure, and give the delegate something to reference), why does it require the creation of two (the closure-supporting object plus the Action delegate)?

*是的,是的,实现细节,我知道!所有我真正的意思是短期内存存储。

推荐答案

根据这一点:CLI(通用语言基础设施)规范说,代理是引用类型。为什么会这样?

The question boils down to this: the CLI (Common Language Infrastructure) specification says that delegates are reference types. Why is this so?

今天.NET Framework中清楚可见一个原因。在原始设计中,有两种代表:普通代表和组播代表,它们的调用列表中可以有多个目标。 MulticastDelegate 类继承自 Delegate 。由于您不能继承值类型,委托必须是引用类型。

One reason is clearly visible in the .NET Framework today. In the original design, there were two kinds of delegates: normal delegates and "multicast" delegates, which could have more than one target in their invocation list. The MulticastDelegate class inherits from Delegate. Since you can't inherit from a value type, Delegate had to be a reference type.

最后所有实际的代表最终都是组播代表,但是在这个过程的这个阶段,合并这两个类是太晚了。请参阅博客文章关于这个确切主题:

In the end, all actual delegates ended up being multicast delegates, but at that stage in the process, it was too late to merge the two classes. See this blog post about this exact topic:


我们放弃了代理和MulticastDelegate
之间的区别,直到V1的结尾。那个时候,这将是一个大规模的
更改,以合并这两个类,所以我们没有这样做。你应该
假装它们被合并,并且只有MulticastDelegate存在。

We abandoned the distinction between Delegate and MulticastDelegate towards the end of V1. At that time, it would have been a massive change to merge the two classes so we didn’t do so. You should pretend that they are merged and that only MulticastDelegate exists.

此外,代表目前有4-6个字段,所有指针。 16字节通常被认为是上限,其中保存内存仍然超过额外的复制。 64位 MulticastDelegate 占用48个字节。鉴于此,以及他们使用遗产的事实表明,一个阶级是自然的选择。

In addition, delegates currently have 4-6 fields, all pointers. 16 bytes is usually considered the upper bound where saving memory still wins out over extra copying. A 64-bit MulticastDelegate takes up 48 bytes. Given this, and the fact that they were using inheritance suggests that a class was the natural choice.

这篇关于为什么是委托参考类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆