使用不受约束传递的泛型类型和约束泛型类型

时间:2016-03-22 03:20:56

标签: c# generics type-inference

假设我们有以下课程:

public interface IFrobnicator<T> { }

class ComparableFrobnicator<T> : IFrobnicator<T> where T : IComparable<T>
{
    public ComparableFrobnicator(T value) { }
}

class EquatableFrobnicator<T> : IFrobnicator<T> where T : IEquatable<T>
{
    public EquatableFrobnicator(T value) { }
}

你可以写方法

public IFrobnicator<T> MakeFrobnicatorFromComparable<T>(T value) where T : IComparable<T>
{
    return new ComparableFrobnicator<T>(value);
}

public IFrobnicator<T> MakeFrobnicatorFromEquatable<T>(T value) where T : IEquatable<T>
{
    return new EquatableFrobnicator<T>(value);
}

如果我想将其统一到一个方法中,最明显的方法是不会编译:

public IFrobnicator<T> MakeFrobnicator<T>(T value)
{
    if (value is IComparable<T>)
    {
        return new ComparableFrobnicator<T>(value);
    }
    else if (value is IEquatable<T>)
    {
        return new EquatableFrobnicator<T>(value);
    }
    else throw new ArgumentException();
}

编译时会引发以下错误:

  

CS0314类型&#39; T&#39;不能用作类型参数&#39; T&#39;在泛型类型或方法&#39; UserQuery.ComparableFrobnicator&#39;。没有拳击转换或类型参数转换来自&#39; T&#39;到了System.IComparable&#39;。

我无法用new ComparableFrobnicator <T>替换new ComparableFrobnicator<IComparable<T>>,因为这会导致问题失败,导致value退出 - 您无法从接口类型转换为具体类型。

相反,我走了反思之路:

public IFrobnicator<T> MakeFrobnicator<T>(T value)
{
    if (value is IComparable<T>)
    {
        var constructor = typeof(ComparableFrobnicator<>).MakeGenericType(typeof(T)).GetConstructor(new[] { typeof(T) });
        return (IFrobnicator<T>)constructor.Invoke(new object[] { value});
    }
    else if (value is IEquatable<T>)
    {
        var constructor = typeof(EquatableFrobnicator<>).MakeGenericType(typeof(T)).GetConstructor(new[] { typeof(T) });
        return (IFrobnicator<T>)constructor.Invoke(new object[] { value });
    }
    else throw new ArgumentException();
}

这似乎工作得很好,但看起来像一个语言倒退了一个像我以前那样强大的类型推断的语言。有什么我想念的东西,还是我错过的更好的技术?

2 个答案:

答案 0 :(得分:2)

尝试扩展方法方法, value.MakeFrobnicator()的实现方式有助于解决泛型类型参数问题

public interface IFrobnicator<T> { }

public class ComparableFrobnicator<T> :IFrobnicator<T> 
                              where T  :IComparable<T> 
 {
    public ComparableFrobnicator(T param) { }
 }

public class EquatableFrobnicator<T> :IFrobnicator<T>
                              where T : IEquatable<T> 
{
   public EquatableFrobnicator(T value) { }
}

 public static class FrobnicatorExtentions
 {
    public static IFrobnicator<T> 
          MakeFrobnicatorFromComparable<T>(this T value)
    where T: IComparable<T>
    {                                                            
      //return new ComparableFrobnicator<T>(value);      
     return value.MakeFrobnicator();
    }

    public static IFrobnicator<T> 
           MakeFrobnicatorFromEquatable<T>(this T value)
    where T : IEquatable<T>
    {
      // return new EquatableFrobnicator<T>(value);
     return value.MakeFrobnicator();
    }  

    public static IFrobnicator<T> 
                  MakeFrobnicator<T>(this IEquatable<T> value) 
    where T: IEquatable<T>
    {
      if (value is T)
      {
        if (value is IEquatable<T>)
        {
          return new EquatableFrobnicator<T>((T)value);
        } 
      }

     throw new InvalidCastException();
    }

   public static IFrobnicator<T> 
                 MakeFrobnicator<T>(this IComparable<T> value) 
   where T : IComparable<T>
  {
     if (value is T)
     {
       if (value is IComparable<T>)
       {
         return new ComparableFrobnicator<T>((T)value);
      }
     }

    throw new InvalidCastException();
  }

}

答案 1 :(得分:0)

你试过演员吗?

public IFrobnicator<T> MakeFrobnicator<T>(T value)
{
    if (value is IComparable<T>)
    {
        return new ComparableFrobnicator<T>((IComparable<T>)value);
    }
    else if (value is IEquatable<T>)
    {
        return new EquatableFrobnicator<T>((IEquatable<T>)value);
    }
    else throw new ArgumentException();
}

如果这完全有效,我不记得我的头脑。无论哪种方式,它都不是非常开放/封闭。您可以改为编写一个转换器列表,每个转换器都决定它们是否适合进行转换。