分配给只读字段的构造函数的简化

Simplification of Constructors Assigning to readonly Fields

我有以下 class:

public class MyCollection<T1, T2> : AnotherCollection<T1>
{
    private readonly ICollection<T2> BaseCollection;

    public MyCollection(IEnumerable<T1> collection, ICollection<T2> baseCollection) : base(collection)
    {
        BaseCollection = baseCollection;
    }

    public MyCollection(List<T1> collection, ICollection<T2> baseCollection) : base(collection)
    {
        BaseCollection = baseCollection;
    }
}

有没有什么方法可以简化这段代码,使 BaseCollection = baseCollection; 只写一次?

IEnumerable 可以同时支持列表和集合。所以你不需要两个构造函数。使用这样的单个构造函数,您可以初始化一个 List 和 IEnumerable

public class MyCollection<T1, T2> : AnotherClass<T1>
{
    private readonly ICollection<T2> BaseCollection;

    public MyCollection(IEnumerable<T1> collection, ICollection<T2> baseCollection) : base(collection)
    {
        BaseCollection = baseCollection;
    }
}

您的初始解决方案是完全可以接受的,特别是如果有基于类型的不同实现。

如果 IEnumerable 和 List 在基础 class 上有不同的实现,则检查类似这样的内容,但不推荐

记住,过度优化是万恶之源

public class AnotherClass<T1>
{
    public AnotherClass(IEnumerable<T1> collection)
    {
        var type = collection.GetType();

        if (type == typeof(IEnumerable<T1>))
        {
            //Do the implementation Detail for IEnumerable
        }
        else if (type == typeof(List<T1>))
        {
            //Do the implementation Detail for List    
        }
        else if (type == typeof(Collection<T1>))
        {
            //Do the implementation Detail for ICollection
        }
        else
        {
            //Do the implementation Detail for Default
        }
    }
}

您可以让第二个构造函数调用第一个构造函数:

public MyCollection(IEnumerable<T1> collection, ICollection<T2> baseCollection) : base(collection)
{
    BaseCollection = baseCollection;
}

public MyCollection(List<T1> collection, ICollection<T2> baseCollection) 
    : this((IEnumerable<T1>)collection, baseCollection)
{
    // you can do additional initialisation for the List<T1> case here
}

注意需要将collection转换为IEnumerable<T1>,这样调用就不会再次解析到同一个构造函数,这是无效的。除了强制转换,您还可以使用 as IEnumerable<T1>AsEnumerable().

如果您希望在调用者传入列表而不是 IEnumerable<T> 时执行一些额外的操作,这可能很有用。如果您不想做任何额外的事情,那么您实际上不需要两个构造函数。 List<T1> 实现了 IEnumerable<T1>,所以你只需要 IEnumerable<T1> 一个。


如果这两个base(collection)调用实际上是在调用基class的不同构造函数(实现不同),那么选择余地不多。我能想到的就是使用 dynamic 并且只有一个这样的构造函数:

public MyCollection(IEnumerable<T1> collection, ICollection<T2> baseCollection) : base((dynamic)collection)
{
    BaseCollection = baseCollection;
}

这将使它根据传入的集合的运行时类型选择调用哪个构造函数。这意味着即使你有一个List<T1>,你也不能选择调用IEnumerable<T1> 基础构造函数,像以前一样将其转换为 IEnumerable<T1>。您必须创建一个新的 IEnumerable,而不是 List

我在这里找到了解决方法:

How do I set a readonly field in an initialize method that gets called from the constructor?

在我的示例中实现为:

public class MyCollection<T1, T2> : AnotherCollection<T1>
{
    private readonly ICollection<T2> BaseCollection;

    public MyCollection(IEnumerable<T1> collection, ICollection<T2> baseCollection) : base(collection)
    {
        Init(baseCollection, out BaseCollection);
    }

    public MyCollection(List<T1> collection, ICollection<T2> baseCollection) : base(collection)
    {
        Init(baseCollection, out BaseCollection);
    }
    
    private void Init(ICollection<T2> baseCollection, out ICollection<T2> baseCollectionAssigment)
    {
        baseCollectionAssigment = baseCollection;
    }
}

我喜欢的一件事是能够拥有一个 Init 方法,在该方法中我可以拥有两个构造函数共有的更多代码。如果我要添加另一个类似的构造函数,我将只需要为其编写一行代码。