Home > database >  Simplification of Constructors Assigning to readonly Fields
Simplification of Constructors Assigning to readonly Fields

Time:03-12

I have the following 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;
    }
}

Is there any way to simplify this code such that BaseCollection = baseCollection; is only written once?

CodePudding user response:

IEnumerable can support both a List and Collection. So you don't need both constructors. With a single constructor like this you can initialize a List and 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;
    }
}

Your initial solution is perfectly acceptable, especially if there are different implementations based on type.

If there's a different implementation on the base class for IEnumerable and List, then check something like this, but not recommended

REMEMBER, OVER OPTIMIZATION IS THE ROOT OF ALL EVIL

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
        }
    }
}

CodePudding user response:

You can make the second constructor call the first constructor:

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
}

Note that it is necessary to convert collection to IEnumerable<T1>, so that the call does not resolve to the same constructor again, which is invalid. Other than a cast, you can also use as IEnumerable<T1> or AsEnumerable().

This could be useful if you want some extra stuff to be done when the caller passes in a list, rather than an IEnumerable<T>. If there's nothing additional that you want to do, you don't actually need two constructors here. List<T1> implements IEnumerable<T1>, so you just need the IEnumerable<T1> one.


If the two base(collection) calls are actually calling different constructors of the base class (with different implementations), then there are not many choices. All I can think of is to use dynamic and have only one constructor like this:

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

This would make it choose which constructor to call based on the runtime type of the collection passed in. This means that even if you have a List<T1>, you can't choose to call the IEnumerable<T1> base constructor by casting it to IEnumerable<T1> like before. You must create a new IEnumerable, that is not a List.

  • Related