类型参数的构造函数签名约束

时间:2011-06-10 08:02:23

标签: c# generics extension-methods

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

问题在于我不能给出新的(TU)约束。

  • 这个问题有解决方法吗?

3 个答案:

答案 0 :(得分:4)

我有两种方法:

首先使用Activator.CreateInstance

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

其次,您可以使用接口来定义属性,而不是使用参数化构造函数:

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

第一种方法有效,但感觉很脏,并且存在使构造函数调用错误的风险,特别是在方法不受限制的情况下。

因此,我认为第二种方法是更好的解决方案。

答案 1 :(得分:3)

也许传入可以从TU创建电视的Func:

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

并致电

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

答案 2 :(得分:1)

也许最简单的方法是在调用位置明确地将TUTV的公式给出,就像之后的选项1一样。如果您希望隐藏场景后面的转换细节以使其在您调用扩展方法的任何地方工作以避免重复公式,那么接口是合适的,因为它可以用作扩展方法的约束:

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
}