将"this"传递给基础构造函数

3
有没有办法将“this”传递给基本构造函数?
abstract class Base<TSelf>
{
    public ICollection<TSelf> List;

    public Base(ICollection<TSelf> list, TSelf self)
    {
        List = list;
        List.Add(self);
    }
}

class Implementation : Base<Implementation>
{
    public Implementation() : base(new List<Implementation>(), this)
    {
    }
}

显然,在Implementation的构造函数中,将this传递给base存在编译错误。
我也没有看到在Base级别实例化list的任何方法。
3个回答

8
不,你不能在构造函数初始化器中使用 this。你需要在之后调用 Add(this) - 但是你可以在 Base<TSelf> 构造函数中这样做,只要将其转换为 TSelf。不幸的是,由于涉及到类型参数允许的转换,你需要先将 this 转换为 object,才能再转换为 TSelf
不过,在 Base 构造函数中,你可以轻松创建一个 List<TSelf>。以下是展示这两种方法的示例代码:
abstract class Base<TSelf>
{
    // Let's make it a property rather than a public field...
    public ICollection<TSelf> List { get; }

    public Base()
    {
        List = new List<TSelf>();
        // This will obviously fail if you try to create a `Base<Foo>`
        // from a class that isn't a Foo
        TSelf selfThis = (TSelf) (object) this;
        List.Add(selfThis);
    }
}

class Implementation : Base<Implementation>
{
}

您可以向 TSelf 添加约束,使强制转换失败的可能性“不太可能是意外的”,但并非完全不可能:
abstract class Base<TSelf> where TSelf : Base<TSelf>

这并不妨碍你的写作

class Implementation : Base<Implementation> {}
class Evil : Base<Implementation> {}

当你实例化一个Evil对象时,你试图将一个Evil引用添加到一个List<Implementation>中,这是不可行的...而且转换失败也无法阻止你进行操作。


在将this强制转换为TSelf之前将其转换为object是解决方案! - Manuel Faux
关于你的评论“这显然会失败”:不能用 where T : Base 来强制执行吗? - Manuel Faux
@ManuelFaux:好的,我会提到。 - Jon Skeet
如果我将 TSelf selfThis = (TSelf) (object) this; 改为 TSelf selfThis = this as TSelf;,在将 TSelf 约束为 class 后会有什么不利影响吗? - Bill Tür stands with Ukraine
1
@Thomas:是的 - 这意味着如果出现问题,你会在列表中默默地得到一个空引用而不是异常。我更喜欢早期失败。 - Jon Skeet

0
正如@Jon Skeet所说,您无法通过常规的构造函数调用传递this。但是,使用反射和FormatterServices类是可能的。下面的示例代码使用工厂模式以获得更好的可读性。

基类实现:

public abstract class Model<TSelf>
    where TSelf : Model<TSelf>
{
    public ICollection<TSelf> Items { get; }

    protected Model(TSelf self, ICollection<TSelf> items)
    {
        if ((self == null) || (items == null))
        {
            throw new ArgumentNullException();
        }

        if (self != this)
        {
            throw new ArgumentException();
        }

        Items = items;
        Items.Add(self);
    }
}

派生类实现:

public sealed class ModelImplementation
    : Model<ModelImplementation>
{
    private ModelImplementation(ModelImplementation self)
        : base(self, new List<ModelImplementation>()) { }
}

这种技术的核心是ModelFactory类,它接受一个未初始化的对象并手动调用适当的构造函数。通过修改GetConstructorInvoke调用,可以实现对其他构造函数参数的支持。

您应该调用ModelFactory.Create<ModelImplementation>()来获取一个新的ModelImplementation,而不是使用new关键字。

工厂类实现:

public static class ModelFactory
{
    public static Model<TSelf> Create<TSelf>()
        where TSelf : Model<TSelf>
    {
        var result = FormatterServices
            .GetUninitializedObject(typeof(TSelf));

        result.GetType()
            .GetConstructor(
                BindingFlags.Instance | BindingFlags.NonPublic,
                null,
                new[] { typeof(TSelf) },
                null)
            .Invoke(
                result,
                new[] { result });

        return (TSelf)result;
    }
}

0

不是人人都知道,但你可以很容易地将子类实例传递给基类构造函数,而不使用向下转换、反射和运行时错误。

为此,您可以添加一个返回TSelf的抽象方法。

请参见以下示例:

abstract class Base<TSelf> where TSelf : Base<TSelf>
{
    public ICollection<TSelf> List;

    public Base(ICollection<TSelf> list)
    {
        List = list;
        List.Add(GetSelf());
    }
    
    protected abstract TSelf GetSelf();
}

class Implementation : Base<Implementation>
{
    public Implementation() : base(new List<Implementation>())
    {
    }
    
    protected override Implementation GetSelf() => this;
}

where TSelf : Base<TSelf> 只是额外的约束条件,而非必须的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接