如何在底层实现C#委托(和字符串)的不变性?

如何在底层实现C#委托(和字符串)的不变性?,c#,.net,.net-core,delegates,cil,C#,.net,.net Core,Delegates,Cil,在C#中,为了以线程安全样式调用委托,我们可以使用以下代码: public static void TestInvokeDelegate1() { CustomClass cc1 = new CustomClass("cc1"); Action action = cc1.WriteName; Action action2 = action; if(action2 != null) { action2(); }

在C#中,为了以线程安全样式调用委托,我们可以使用以下代码:

public static void TestInvokeDelegate1()
{
    CustomClass cc1 = new CustomClass("cc1");
    Action action = cc1.WriteName;

    Action action2 = action;
    if(action2 != null)
    {
        action2();
    }
}
或者,在C#6.0或更高版本中,我们还可以使用:

public static void TestInvokeDelegate2()
{
    CustomClass cc1 = new CustomClass("cc1");
    Action action = cc1.WriteName;

    action?.Invoke();
}
我们知道,这些代码使用委托的不变性来实现线程安全。但是在.net编译器或运行时中如何实现不变性呢?我使用Ildasm工具获取这两种方法的IL代码:

.method public hidebysig static void  TestInvokeDelegate1() cil managed
{
  //        32 (0x20)
  .maxstack  2
  .locals init (class [System.Runtime]System.Action V_0)
  IL_0000:  ldstr      "cc1"
  IL_0005:  newobj     instance void ess_cs.CustomClass::.ctor(string)
  IL_000a:  ldftn      instance void ess_cs.CustomClass::WriteName()
  IL_0010:  newobj     instance void [System.Runtime]System.Action::.ctor(object,
                                                                          native int)
  IL_0015:  stloc.0
  IL_0016:  ldloc.0
  IL_0017:  brfalse.s  IL_001f
  IL_0019:  ldloc.0
  IL_001a:  callvirt   instance void [System.Runtime]System.Action::Invoke()
  IL_001f:  ret
} // end of method Program::TestInvokeDelegate1
以及

我不精通IL,但在这些IL代码中,我看到的只是复制对对象的引用。通过简单地复制引用,两个变量指向同一个对象,不变性在哪里?没有复制对象数据的代码,它是如何实现的?(还有字符串的不变性)


附加文本:我为测试字符串和委托的不变性而编写的代码:

(注意:我知道下面代码C#级别的每一件事,我编写它是为了测试字符串或委托的不变性。当指向它的变量被分配给另一个变量时,似乎对象被复制了。但是在IL代码中,我找不到对象复制代码。我想知道的是e不变性,在编译器或运行时(clr)级别。后面发生了什么?

以及


自定义类类:

class CustomClass
{
    private string m_name = null;

    public CustomClass(string name)
    {
        m_name = name;
    }

    public void WriteName()
    {
        Console.WriteLine(m_name);
    }
}

看看我的解释:

public static void TestDelegateImmutable()
{
    CustomClass cc1 = new CustomClass("cc1");
    CustomClass cc2 = new CustomClass("cc2");

    Action action = cc1.WriteName; //Allocate action to an object refers to cc1.WriteName.
    action += cc2.WriteName; //Allocate action to an new object refers to cc1.WriteName and cc2.WriteName.
    Action action2 = action; //Allocate action2 to the object of action has been created above.
    action -= cc2.WriteName; //Allocate action to an new object refers to cc1.WriteName.

    action(); //Output: "cc1"
    action2(); //Output: "cc1" and "cc2"

    action -= cc1.WriteName; //Allocate action to null.
    Console.WriteLine(action == null); //true
    Console.WriteLine(action2 == null); //false
    Console.WriteLine(action2.GetInvocationList().Length); //2
}

希望这对您有意义。

正如我们所知,这些代码使用委托的不变性来实现线程安全。
我不知道这一点。您能给我指一些关于这一点的文档吗?
在C#中,为了以线程安全的样式调用委托,我们可以使用以下代码:
您所说的“线程安全样式”是什么意思?是否可以轻松地对委托的实例进行变异?
TestStringImmutable
决不能证明字符串是不可变的。用于字符串的代码对所有类型的行为都是相同的。如果创建对象,请将其赋给变量,然后将该变量复制到第二个变量,然后(直接)赋给新对象对于第一个变量(即,您正在为该变量赋值,而不仅仅是在其上设置属性),则第二个变量将与第一个变量不同。这不是不可变的,因为每种类型(甚至是可变的类型)这样做。类似地,
action-=cc2.WriteName;
并没有改变分配给它的对象,而是分配了一个新的值。它相当于
action=action-cc2.WriteName;
。注意分配。-注意它说它返回了一个新的委托。另外,请注意,我从来没有说过任何关于线程安全的事情或者说是不可更改的。因为它与您提供的代码无关。您的代码对我来说没有任何意义,它与我提出的问题无关。@DingXin阅读了我上面的评论,然后重新阅读这个答案。因为这个答案是正确的。
public static void TestDelegateImmutable()
{
    CustomClass cc1 = new CustomClass("cc1");
    CustomClass cc2 = new CustomClass("cc2");

    Action action = cc1.WriteName;
    action += cc2.WriteName;
    Action action2 = action;
    action -= cc2.WriteName;

    action(); //Output: "cc1"
    action2(); //Output: "cc1" and "cc2"

    action -= cc1.WriteName;
    Console.WriteLine(action == null); //true
    Console.WriteLine(action2 == null); //false
    Console.WriteLine(action2.GetInvocationList().Length); //2
}
class CustomClass
{
    private string m_name = null;

    public CustomClass(string name)
    {
        m_name = name;
    }

    public void WriteName()
    {
        Console.WriteLine(m_name);
    }
}
public static void TestDelegateImmutable()
{
    CustomClass cc1 = new CustomClass("cc1");
    CustomClass cc2 = new CustomClass("cc2");

    Action action = cc1.WriteName; //Allocate action to an object refers to cc1.WriteName.
    action += cc2.WriteName; //Allocate action to an new object refers to cc1.WriteName and cc2.WriteName.
    Action action2 = action; //Allocate action2 to the object of action has been created above.
    action -= cc2.WriteName; //Allocate action to an new object refers to cc1.WriteName.

    action(); //Output: "cc1"
    action2(); //Output: "cc1" and "cc2"

    action -= cc1.WriteName; //Allocate action to null.
    Console.WriteLine(action == null); //true
    Console.WriteLine(action2 == null); //false
    Console.WriteLine(action2.GetInvocationList().Length); //2
}