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

本文关键字:约束 类型参数 构造函数 | 更新日期: 2023-09-27 17:58:55

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

问题是我不能给出新的(TU(约束。

  • 这个问题有什么解决办法吗

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

我有两种方法:

首先使用Activator。创建实例

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

第一个方法可以工作,但感觉很脏,并且存在错误调用构造函数的风险,特别是当方法不受约束时。

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

也许传入一个Func,它可以从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));
}

并与通话

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

也许最简单的方法是在调用位置显式地给出从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
}