简体   繁体   中英

Constructor Signature Constraints on Type Parameters

public static IEnumerable<TV> To<TU, TV>(this IEnumerable<TU> source) where TV : new(TU)
{
    return source.Select(x => new TV(TU));
}

The problem is that I can't give the new(TU) constraints.

  • Is there any solution to this problem?

I've got two approaches for you:

Firstly using Activator.CreateInstance

public static IEnumerable<TV> To<TU, TV>(this IEnumerable<TU> source)
{ 
  return source.Select(m => (TV) Activator.CreateInstance(typeof(TV), m));
}

Secondly, you could use interfaces to define properties rather than use parameterized constructors:

public interface IRequiredMember
{}

public interface IHasNeccesaryMember
{
  IRequiredMember Member
  {
    get;
    set;
  }
}

public static IEnumerable<TV> To<TU, TV>(this IEnumerable<TU> source)
            where TV : IHasNeccesaryMember, new()
            where TU : IRequiredMember
 {
    return source.Select(m => new TV{ Member = m });
 }

The first method works but feels dirty and there is the risk of getting the constructor call wrong, particularly as the method is not constrained.

Therefore, I think the second method is a better solution.

Maybe pass in a Func which can create a TV from a TU:

public static IEnumerable<TV> To<TU, TV>(
    this IEnumerable<TU> source, 
    Func<TU, TV> builder)
    where TV : class
{
    return source.Select(x => builder(x));
}

and call with

tus.To(x => new TV(x));

Maybe the easiest way is to explicitly give the formula from TU to TV at the calling place like option 1 here after. If you prefer to hide the details of the transformation behind the scene to have it work anywhere where you call the extension method to avoid repeating the formula, then an interface is appropriate since it can be used as a constraint for an extension method:

class Program
{
    static void Main(string[] args)
    {
        IEnumerable<U> uSequence = new List<U>();
        IEnumerable<V> vSequence1 = uSequence.To(u => new V(u)); //Option 1 : explicit transformation, needs neither any interface nor explicit types (type inference at work)
        IEnumerable<V> vSequence2 = uSequence.To<U, V>(); //Option 2 : implicit transformation internally supported from U to V by type V thanks to IBuildableFrom<TV, TU>, but you must precise To<U, V>() with the types
    }
}

public static class Extensions    {
    //Option 1
    public static IEnumerable<TV> To<TU, TV>(this IEnumerable<TU> source, Func<TU,TV> transform)
    {
        return source.Select(tu => transform(tu));
    }

    //Option 2
    public static IEnumerable<TV> To<TU, TV>(this IEnumerable<TU> source) where TV : IBuildableFrom<TV, TU>, new()
    {
        return source.Select(tu => new TV().BuildFrom(tu));
    }
}

public interface IBuildableFrom<TV, TU>
{
    TV BuildFrom(TU tu);
}

public class U { } //Cheesy concrete class playing the rôle of TU
public class V : IBuildableFrom<V, U> //Cheesy concrete class playing the rôle of TV
{
    public V BuildFrom(U u)
    {
        //Initialization of this' properties based on u's ones
        return this;
    }

    public V(U u) { }//Used by option 1
    public V() { } //Used by option 2
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM