'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#中的Class
和Struct
类比。他们是完全不同的。
假设我得到了以下代码:
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# 编写。
从根本上说,类 总是在堆上分配,堆内存总是在执行构造函数之前由运行时初始化为零。可以在堆栈上分配结构,其中内存未初始化以提高性能。因此,结构的字段具有类似于局部变量的语义。编译器强制执行此规则以确保所有结构字段必须在使用前分配。
我在这种情况下处理一个问题:
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#中的Class
和Struct
类比。他们是完全不同的。
假设我得到了以下代码:
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# 编写。
从根本上说,类 总是在堆上分配,堆内存总是在执行构造函数之前由运行时初始化为零。可以在堆栈上分配结构,其中内存未初始化以提高性能。因此,结构的字段具有类似于局部变量的语义。编译器强制执行此规则以确保所有结构字段必须在使用前分配。