在任何地方调用基类型构造函数

我最近接受了一次采访,其中还有一个关于在C#中调用构造函数的顺序的问题。回答后,受访者决定证明自己的学识,并表示在Java中,可以在派生类型的构造函数中的任何位置调用基本类型的构造函数,而C#当然会失败。



事实证明这是个谎言,谎言和挑衅
image



但这不再重要,因为挑战已被接受。



图片



免责声明
. . . .



训练



我们创建一个继承链。为简单起见,我们将使用无参数构造函数。在构造函数中,我们将显示有关调用它的对象的类型和标识符的信息。



public class A
{
    public A()
    {
        Console.WriteLine($"Type '{nameof(A)}' .ctor called on object #{GetHashCode()}");
    }
}

public class B : A
{
    public B()
    {
        Console.WriteLine($"Type '{nameof(B)}' .ctor called on object #{GetHashCode()}");
    }
}

public class C : B
{
    public C()
    {
        Console.WriteLine($"Type '{nameof(C)}' .ctor called on object #{GetHashCode()}");
    }
}


运行程序:



class Program
{
    static void Main()
    {
        new C();
    }
}


然后我们得到输出:



Type 'A' .ctor called on object #58225482

Type 'B' .ctor called on object #58225482

Type 'C' .ctor called on object #58225482



抒情离题
, . , . , . :



public A() : this() { } // CS0516  Constructor 'A.A()' cannot call itself


:



public A() : this(new object()) { }
public A(object _) : this(0) { }
public A(int _) : this() { } // CS0768  Constructor 'A.A(int)' cannot call itself through another constructor


删除重复的代码



添加一个助手类:



internal static class Extensions
{
    public static void Trace(this object obj) =>
        Console.WriteLine($"Type '{obj.GetType().Name}' .ctor called on object #{obj.GetHashCode()}");
}


我们替换所有构造函数



Console.WriteLine($"Type '{nameof(...)}' .ctor called on object #{GetHashCode()}");






this.Trace();


但是,该程序现在输出: 在我们的例子中,可以使用以下技巧。谁知道编译时间类型?编译器。它还根据这些类型选择方法重载。对于通用类型和方法,它也生成构造的实体。因此,我们通过重写Trace方法返回正确的类型推断,如下所示:



Type 'C' .ctor called on object #58225482

Type 'C' .ctor called on object #58225482

Type 'C' .ctor called on object #58225482







public static void Trace<T>(this T obj) =>
    Console.WriteLine($"Type '{typeof(T).Name}' .ctor called on object #{obj.GetHashCode()}");


访问基本类型构造函数



这就是反思的源泉。扩展添加方法:



public static Action GetBaseConstructor<T>(this T obj) =>
    () => typeof(T)
          .BaseType
          .GetConstructor(Type.EmptyTypes)
          .Invoke(obj, Array.Empty<object>());


将属性添加到BC 类型



private Action @base => this.GetBaseConstructor();


在任何地方调用基类型构造函数



将构造函数BC的内容更改为:



this.Trace();
@base();


现在输出看起来像这样:



Type 'A' .ctor called on object #58225482

Type 'B' .ctor called on object #58225482

Type 'A' .ctor called on object #58225482

Type 'C' .ctor called on object #58225482

Type 'A' .ctor called on object #58225482

Type 'B' .ctor called on object #58225482

Type 'A' .ctor called on object #58225482



更改基本类型构造函数的调用顺序



在类型A内,创建一个帮助程序类型:



protected class CtorHelper
{
    private CtorHelper() { }
}


由于此处仅语义很重要,因此建议将类型构造函数设为私有。实例化没有任何意义。该类型仅用于区分类型A构造函数的重载和从其派生的重载出于相同的原因,该类型应放在A内并使其受到保护。



适当的构造函数添加到ABC



protected A(CtorHelper _) { }
protected B(CtorHelper _) { }
protected C(CtorHelper _) { }


对于类型BC,向所有构造函数添加调用:



: base(null)


结果,类应如下所示
internal static class Extensions
{
    public static Action GetBaseConstructor<T>(this T obj) =>
        () => typeof(T)
        .BaseType
        .GetConstructor(Type.EmptyTypes)
        .Invoke(obj, Array.Empty<object>());

    public static void Trace<T>(this T obj) =>
        Console.WriteLine($"Type '{typeof(T).Name}' .ctor called on object #{obj.GetHashCode()}");
}

public class A
{
    protected A(CtorHelper _) { }

    public A()
    {
        this.Trace();
    }

    protected class CtorHelper
    {
        private CtorHelper() { }
    }
}

public class B : A
{
    private Action @base => this.GetBaseConstructor();

    protected B(CtorHelper _) : base(null) { }

    public B() : base(null)
    {
        this.Trace();
        @base();
    }
}

public class C : B
{
    private Action @base => this.GetBaseConstructor();

    protected C(CtorHelper _) : base(null) { }

    public C() : base(null)
    {
        this.Trace();
        @base();
    }
}


输出为:



Type 'C' .ctor called on object #58225482

Type 'B' .ctor called on object #58225482

Type 'A' .ctor called on object #58225482



天真的Simpleton认为编译器作弊
image



了解结果



通过向Extensions添加方法:



public static void TraceSurrogate<T>(this T obj) =>
    Console.WriteLine($"Type '{typeof(T).Name}' surrogate .ctor called on object #{obj.GetHashCode()}");


并在接受CtorHelper的所有构造函数中调用它,我们得到 以下输出:当然,根据基本/派生原理的构造函数的顺序没有改变。但是,由于通过调用客户端无法访问的助手构造函数进行了重定向,因此可用于承载语义负载的客户端代码可用的构造函数的顺序已更改。



Type 'A' surrogate .ctor called on object #58225482

Type 'B' surrogate .ctor called on object #58225482

Type 'C' .ctor called on object #58225482

Type 'A' surrogate .ctor called on object #58225482

Type 'B' .ctor called on object #58225482

Type 'A' .ctor called on object #58225482






All Articles