'this' 对象在其所有字段都分配给之前不能使用。为什么 class 构造函数没有这样的限制?

The 'this' object cannot be used before all of its fields are assigned to. Why do not class constructors have such restriction?

我在这种情况下处理一个问题:

public struct RequestLog
{
    public RequestLog(string body) // :this() - solution to the problem
    {
        Body = body != null ? Limit(body) : null; // The 'this' object cannot be used before all of its fields are assigned to
    }

    public readonly string Body;

    private string Limit(string str, int limit = 100) =>
        str.Length > limit
            ? str.Substring(0, limit)
            : str;
}

我知道如果我在调用我的构造函数之前调用无参数结构构造函数,我不会有这个问题。

但是如果我将 RequestLog 类型从结构更改为 class,代码将是正确的。

我的问题:为什么我使用结构会出现此错误,而使用 class 则不会出现此错误。如果我是对的,struct parameterless constructor 会初始化所有字段,也许我可以解释一下问题:

为什么我应该在创建结构时显式初始化所有字段,而在创建 class 时不应该这样做。

您不能使用未初始化对象的方法。 C# 中的每个非静态方法调用都是一个使用的对象。所以调用 Limit(body) 等于 this.Limit(body)。要编译您的代码,您应该将 Limit 方法更改为静态方法。

首先,尽量不要拿C#中的ClassStruct类比。他们是完全不同的。

假设我得到了以下代码:

 class MyClass
    {
        private int a = 0;
    }
    struct MyStruct
    {
        //private int a = 0; => this is not allowed
    }
    class Program
    {
        static void Main(string[] args)
        {
            var aCls=new MyClass();
            var aStruct=new MyStruct();
        }
    }

当您检查il代码时, class MyClass{...} 生成了以下代码:

.class private auto ansi beforefieldinit
  Ctors.MyClass
    extends [mscorlib]System.Object
{

  .field private int32 a

  .method public hidebysig specialname rtspecialname instance void
    .ctor() cil managed
  {
    .maxstack 8

    // [11 9 - 11 27]
    IL_0000: ldarg.0      // this
    IL_0001: ldc.i4.0
    IL_0002: stfld        int32 Ctors.MyClass::a
    IL_0007: ldarg.0      // this
    IL_0008: call         instance void [mscorlib]System.Object::.ctor()
    IL_000d: nop
    IL_000e: ret

  } // end of method MyClass::.ctor
} // end of class Ctors.MyClass

struct MyStruct{} 生成了以下代码:

.class private sealed sequential ansi beforefieldinit
  Ctors.MyStruct
    extends [mscorlib]System.ValueType
{
} // end of class Ctors.MyStruct

根据以上观察:
1.MyClass生成了一个无参构造函数.
2. a=0赋值会放在无参构造函数.
3.没有为MyStruct
自动生成无参数构造函数 4. a=0赋值不能直接放在MyStruct中。它不会编译。

让我们看看当我们new他们时会发生什么:

    IL_0001: newobj       instance void Ctors.MyClass::.ctor()
    IL_0006: stloc.0      // aCls

    // [22 13 - 22 40]
    IL_0007: ldloca.s     aStruct
    IL_0009: initobj      Ctors.MyStruct

MyClass 将由 newobj 存储,而 MyStruct 将由 initobj 存储。

为什么调用this()解决了? Actullay,它生成了以下代码:

    IL_0000: ldarg.0      // this
    IL_0001: initobj      Ctors.RequestLog

我觉得不应该考虑无参构造函数。它的工作方式与您对 无参数构造函数 的预期非常不同。它只是为值类型清除状态的语法糖。

为什么要显式初始化 struct 的所有字段?简单安全。 可以在以下引用中找到更多详细信息:

Note Strictly speaking, value type fields are guaranteed to be 0/null when the value type is a field nested within a reference type. However, stack-based value type fields are not guaranteed to be 0/null . For verifiability, any stack-based value type field must be written to prior to being read. If code could read a value type’s field prior to writing to the field, a security breach is possible. C# and other compilers that produce verifiable code ensure that all stack-based value types have their fields zeroed out or at least written to before being read so that a verification exception won’t be thrown at run time. For the most part, this means that you can assume that your value types have their fields initialized to 0 , and you can completely ignore everything in this note.

Jeffrey Richter CLR 通过 C# 编写。

从根本上说,类 总是在堆上分配,堆内存总是在执行构造函数之前由运行时初始化为零。可以在堆栈上分配结构,其中内存未初始化以提高性能。因此,结构的字段具有类似于局部变量的语义。编译器强制执行此规则以确保所有结构字段必须在使用前分配。