2016-03-22 5 views
2

Допустим, мы имеем следующие классы:Использование универсального типа передается без ограничений с ограничениями общего типа

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

Это вызывает следующую ошибку при компиляции led:

CS0314 Тип 'T' не может использоваться как параметр типа 'T' в общем типе или методе 'UserQuery.ComparableFrobnicator'. Нет конверсии бокса или преобразования параметров типа из «T» в «System.IComparable».

Я не могу заменить 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(); 
} 

Это, кажется, работает отлично, но выглядит как шаг назад в языке с такими умозаключениями мощного типа, как я привык. Есть что-то, чего я пропускаю, или лучшая техника, которую я пропустил?

+0

В вашем комбинированном методе значение типа T в качестве параметра, то вы видите, является ли тип значения IComparable или IEquatable . Что-то там не так. – David

+3

@ Давид, например. 'int', является' IComparable '* и *' IEquatable '. Тип, представляющий комплексные числа, может быть приравнен к себе, но не сопоставим. – RoadieRich

+0

Кажется, что последний метод должен быть ограничен IComparable –

ответ

2

Попробуйте подход методы удлинители, Путь value.MakeFrobnicator() реализован помогает решить родового типа Params Issue

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

} 
+0

Кажется, что все работает. Теперь, чтобы узнать, могу ли я расширить его до моего конкретного случая. – RoadieRich

+0

Вы можете удалить значение 'value is IComparable ' etc: они всегда верны подписи метода. – RoadieRich

+0

@RoadieRich спасибо, что принял мой ответ, согласен 'значение IComparable ' слева от старого цикла рефакторинга –

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

Я не могу вспомнить с головы, если это действительно так. В любом случае, это не очень открыто/закрыто. Вместо этого вы можете написать список конвертеров, каждый из которых решит, являются ли они подходящим типом для преобразования.

+0

Это тоже не скомпилируется. 'ComparableFrobnicator ' является проблемой. – RoadieRich

+0

Использование 'is', тогда литье выполняет дополнительные операции.Вместо этого сделайте 'var compare = value как IComparable; если (сопоставимо! = null) '. – ErikE

Смежные вопросы