So, today I reflected an arbitary .NET assembly using ILSpy + dotPeek to gain deeper insight about how IL code works when I stumbled upon this weird part (dummy example):
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);
}
}
As far as I can see, the derived class doesn't call the base constructor in the first place, but instead does something with onlyOneInstance
and then calls be base constructor.
My question is: Is it possible to call the base constructor explicitly in C# after some work has been done? Or is this only possible in IL? I know it's being easily done in e.g. Java using super()
, however I've never seen it in .NET.
EDIT
I just had a talk with my boss and he's okay with posting some real world code of the library (it's one of our companys internal):
**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>)
You can do this:
public class SomeDerivedClass : SomeBaseClass {
public SomeDerivedClass(SomeType onlyOneInstance)
: base(new[] { onlyOneInstance})
{
}
}
In other words, you definitely can run code prior to the base constructor as part of constructing the parameters passed to it. In this case, we're constructing an array to pass to the base class. You can also call static methods, as recursive mentions.
I missed the null check. Apparently they want to preserve the null passing case rather than an array containing null. That would be the equivalent of :
public class SomeDerivedClass : SomeBaseClass {
public SomeDerivedClass(SomeType onlyOneInstance)
: base(onlyOneInstance != null ? new [] { onlyOneInstance} : null)
{
}
}