为什么代表们引用类型?类型、代表

2023-09-02 20:48:45 作者:姑娘我天生傲骨怎能服输

快速笔记上公认的答案:我不同意的Jeffrey's回答,即点即自代理不得不说是一个引用类型,它遵循所有代表是引用类型。 (这根本不是真的,一个多层次的继承链中的规则出来的值类​​型;所有的枚举类型,例如,从 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;

我创建的新的动作对象,每一次。我敢肯定的成本是最小的,但它涉及的内存分配到后来被垃圾收集。

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.

由于代表是天生的自身的不可变的,我不知道为什么他们不能值类型?然后一行code像上面的人会产生什么比一个简单的赋值给一个内存地址在堆栈上*。

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(); };

在这种情况下,确实构成的关闭的,是的。在许多情况下,我想这确实需要一个实际的引用类型(当本地变量被关闭了,例如和封闭内被修改)。 但是,在某些情况下,它不应该。例如,在上述情况下,这似乎是一个类型,支持关闭可能是这样的: 我收回我原来的观点这一点。下面确实需要引用类型(或:它没有的需要的,但如果它是一个结构它只是要得到反正盒装)。于是,不顾低于code例子。我离开它只是提供上下文答案specfically提到它。

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.

更新:在回答阿尼的评论,我明白为什么编译器生成输入我上面的例子中很可能会成为一个引用类型,因为如果委托是要包含一个函数指针和对象指针它需要一个引用类型呢(至少在使用闭包匿名函数,即使你推出一个额外的泛型类型,因为参数,例如,动作< 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(); };

...需要,那为什么还需要创建两个(封支撑物的以及的的动作委托)?

*是的,是的,实施的细节,我知道了!我真正的意思是的的短期记忆存储的

推荐答案

现在的问题归结为一点: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 类继承自代理。因为你不能从一个值类型继承,代理不得不说是一个引用类型。

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的末端。在那个时候,那将是一个大规模   更改合并两个班,所以我们并没有这样做。你应该   他们合并和pretend只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.

 
精彩推荐
图片推荐