C# 中的显式构造函数调用
Explicit constructor call in C#
所以,今天我使用 ILSpy + dotPeek 反映了一个任意的 .NET 程序集,以便在我偶然发现这个奇怪的部分(虚拟示例)时更深入地了解 IL 代码的工作原理:
public class SomeBaseClass {
public SomeBaseClass(SomeType[] iExpectACollection) {
...
}
}
public class SomeDerivedClass {
public SomeDerivedClass(SomeType onlyOneInstance) {
SomeType[] collection;
if(onlyOneInstance != null)
collection = new SomeType[] { onlyOneInstance };
base.\u002Ector(collection);
}
}
据我所知,派生的 class 首先不调用基本构造函数,而是用 onlyOneInstance
和 then 调用基础构造函数。
我的问题是:是否可以在完成一些工作后在 C# 中显式调用基本构造函数?或者这只能在 IL 中实现?我知道这很容易完成,例如Java 使用 super()
,但我从未在 .NET 中看到它。
编辑
我刚刚和我的老板谈过,他同意发布一些真实世界的库代码(这是我们公司的内部代码之一):
**IL PART**
.method public hidebysig specialname rtspecialname
instance void .ctor (
string contextId,
class MyComp.NetStack.BufferManager bufferManager,
class MyComp.NetStack.TcpChannelQuotas quotas,
class [System]System.Security.Cryptography.X509Certificates.X509Certificate2 clientCertificate,
class [System]System.Security.Cryptography.X509Certificates.X509Certificate2[] clientCertificateChain,
class [System]System.Security.Cryptography.X509Certificates.X509Certificate2 serverCertificate,
class MyComp.NetStack.EndpointDescription endpoint,
class MyComp.NetStack.ApplicationThreadPool threadPool
) cil managed
{
// Method begins at RVA 0x648e0
// Code size 263 (0x107)
.maxstack 10
.locals init (
[0] class MyComp.NetStack.EndpointDescription[]
)
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: ldarg.2
IL_0003: ldarg.3
IL_0004: ldarg.s serverCertificate
IL_0006: ldarg.s clientCertificateChain
IL_0008: ldarg.s endpoint
IL_000a: brtrue.s IL_000f
IL_000c: ldnull
IL_000d: br.s IL_0021
IL_000f: ldc.i4.1
IL_0010: newarr MyComp.NetStack.EndpointDescription
IL_0015: stloc.0
IL_0016: ldloc.0
IL_0017: ldc.i4.0
IL_0018: ldarg.s endpoint
IL_001a: stelem.ref
IL_001b: ldloc.0
IL_001c: newobj instance void MyComp.NetStack.EndpointDescriptionCollection::.ctor(class [mscorlib]System.Collections.Generic.IEnumerable`1<class MyComp.NetStack.EndpointDescription>)
可能发生这种情况的一种方式是字段初始化程序逻辑。
另一种实现它的方法是为基本构造函数参数值调用静态方法。
class Base {
public Base(object value) {
Console.WriteLine ("Base constructor");
}
}
class Child : Base {
public Child() : base(DoWorkBeforeBaseConstructor()) { }
private static object DoWorkBeforeBaseConstructor() {
Console.WriteLine ("doing work");
return null;
}
}
你可以这样做:
public class SomeDerivedClass : SomeBaseClass {
public SomeDerivedClass(SomeType onlyOneInstance)
: base(new[] { onlyOneInstance})
{
}
}
换句话说,您绝对可以 运行 在基本构造函数之前编写代码,作为构造传递给它的参数的一部分。在这种情况下,我们正在构造一个数组以传递给基数 class。也可以调用静态方法,递归提到。
我错过了空检查。显然他们想要保留 null passing case 而不是包含 null 的数组。这相当于 :
public class SomeDerivedClass : SomeBaseClass {
public SomeDerivedClass(SomeType onlyOneInstance)
: base(onlyOneInstance != null ? new [] { onlyOneInstance} : null)
{
}
}
补充其他答案:
CLR 允许您在调用基础 class 构造函数之前 运行 任意内容。 CLR 强制您在所有可能的路径中通过该方法只调用一次基本 class 构造函数。我最近尝试使用 peverify
.
如您所见,C# 实施了其他限制。如果使用不同的语言编译此程序集,则仅适用 CLR 规则。甚至那些也不适用 SkipVerification
代码,我相信,现在几乎所有代码都是 运行。
所以,今天我使用 ILSpy + dotPeek 反映了一个任意的 .NET 程序集,以便在我偶然发现这个奇怪的部分(虚拟示例)时更深入地了解 IL 代码的工作原理:
public class SomeBaseClass {
public SomeBaseClass(SomeType[] iExpectACollection) {
...
}
}
public class SomeDerivedClass {
public SomeDerivedClass(SomeType onlyOneInstance) {
SomeType[] collection;
if(onlyOneInstance != null)
collection = new SomeType[] { onlyOneInstance };
base.\u002Ector(collection);
}
}
据我所知,派生的 class 首先不调用基本构造函数,而是用 onlyOneInstance
和 then 调用基础构造函数。
我的问题是:是否可以在完成一些工作后在 C# 中显式调用基本构造函数?或者这只能在 IL 中实现?我知道这很容易完成,例如Java 使用 super()
,但我从未在 .NET 中看到它。
编辑
我刚刚和我的老板谈过,他同意发布一些真实世界的库代码(这是我们公司的内部代码之一):
**IL PART**
.method public hidebysig specialname rtspecialname
instance void .ctor (
string contextId,
class MyComp.NetStack.BufferManager bufferManager,
class MyComp.NetStack.TcpChannelQuotas quotas,
class [System]System.Security.Cryptography.X509Certificates.X509Certificate2 clientCertificate,
class [System]System.Security.Cryptography.X509Certificates.X509Certificate2[] clientCertificateChain,
class [System]System.Security.Cryptography.X509Certificates.X509Certificate2 serverCertificate,
class MyComp.NetStack.EndpointDescription endpoint,
class MyComp.NetStack.ApplicationThreadPool threadPool
) cil managed
{
// Method begins at RVA 0x648e0
// Code size 263 (0x107)
.maxstack 10
.locals init (
[0] class MyComp.NetStack.EndpointDescription[]
)
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: ldarg.2
IL_0003: ldarg.3
IL_0004: ldarg.s serverCertificate
IL_0006: ldarg.s clientCertificateChain
IL_0008: ldarg.s endpoint
IL_000a: brtrue.s IL_000f
IL_000c: ldnull
IL_000d: br.s IL_0021
IL_000f: ldc.i4.1
IL_0010: newarr MyComp.NetStack.EndpointDescription
IL_0015: stloc.0
IL_0016: ldloc.0
IL_0017: ldc.i4.0
IL_0018: ldarg.s endpoint
IL_001a: stelem.ref
IL_001b: ldloc.0
IL_001c: newobj instance void MyComp.NetStack.EndpointDescriptionCollection::.ctor(class [mscorlib]System.Collections.Generic.IEnumerable`1<class MyComp.NetStack.EndpointDescription>)
可能发生这种情况的一种方式是字段初始化程序逻辑。
另一种实现它的方法是为基本构造函数参数值调用静态方法。
class Base {
public Base(object value) {
Console.WriteLine ("Base constructor");
}
}
class Child : Base {
public Child() : base(DoWorkBeforeBaseConstructor()) { }
private static object DoWorkBeforeBaseConstructor() {
Console.WriteLine ("doing work");
return null;
}
}
你可以这样做:
public class SomeDerivedClass : SomeBaseClass {
public SomeDerivedClass(SomeType onlyOneInstance)
: base(new[] { onlyOneInstance})
{
}
}
换句话说,您绝对可以 运行 在基本构造函数之前编写代码,作为构造传递给它的参数的一部分。在这种情况下,我们正在构造一个数组以传递给基数 class。也可以调用静态方法,递归提到。
我错过了空检查。显然他们想要保留 null passing case 而不是包含 null 的数组。这相当于 :
public class SomeDerivedClass : SomeBaseClass {
public SomeDerivedClass(SomeType onlyOneInstance)
: base(onlyOneInstance != null ? new [] { onlyOneInstance} : null)
{
}
}
补充其他答案:
CLR 允许您在调用基础 class 构造函数之前 运行 任意内容。 CLR 强制您在所有可能的路径中通过该方法只调用一次基本 class 构造函数。我最近尝试使用 peverify
.
如您所见,C# 实施了其他限制。如果使用不同的语言编译此程序集,则仅适用 CLR 规则。甚至那些也不适用 SkipVerification
代码,我相信,现在几乎所有代码都是 运行。