2010-02-23 3 views
6

Я создаю пользовательский общий класс:C# Общие типы Причина неоднозначности

class Widget< T1, T2> 
{ 
    ... 
    public bool Bar(T1 type1) 
    { 
     ... 
    } 
    public bool Bar(T2 type2) 
    { 
     ... 
    } 
    ... 
} 

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

Widget<int, int> Foo = new Widget<int, int>(); 
... 
Foo.Bar(5); 
... 

Есть ли способ обойти это ? Есть ли предложение, которое я могу поставить по строкам «где: TypeOf (T1)! = TypeOf (T2)» или любым способом сделать это однозначным? Предпочтительно, int, int будет доступен, но он не является мандатом.

Update:

Я на самом деле сам по себе обнаружил приемлемое решение (для меня) к этой проблеме, для тех, кто заинтересован

class Widget< T1, T2> 
{ 
    ... 
    public bool Bar(object o) 
    { 
     if(o.GetType() == typeof(T1)) 
     { 
      ... 
     } 
     if(o.GetType() == typeof(T2)) 
     { 
      ... 
     } 
    } 
    ... 
} 
+0

Мне интересно, что это за реальный случай мира ... – user76035

+0

в реальности это становится и недействительным метод при загрузке – Perpetualcoder

+1

@wwosik: словарь с 2 ключами. –

ответ

18

Is there a clause that I can put along the lines of "where : TypeOf(T1) != TypeOf(T2)"

Вы можете заставить своего конструктора исключить исключение во время выполнения. Но нет возможности предотвратить эту ситуацию на время компиляции.

Any way to make this unambiguous?

Вы должны изменить имена своих методов, чтобы они не сталкивались. Это, безусловно, самая безопасная и простая вещь.

Фактически, IIRC CLR оставляет за собой право не создавать тип, который создает двусмысленность в сигнатурах методов, подобных этому. (Очевидно, что наша реализация на самом деле преуспевает, но вы наступаете на очень тонкий лед, когда вы вытаскиваете такие виды махинаций.)

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

http://blogs.msdn.com/ericlippert/archive/2006/04/05/odious-ambiguous-overloads-part-one.aspx

http://blogs.msdn.com/ericlippert/archive/2006/04/06/odious-ambiguous-overloads-part-two.aspx

Также обратите внимание, что компилятор будет остановить вас от создания такого типа, что он реализует два интерфейсов, которые могут быть одинаковыми в разработке. Это незаконно:

class C<T, U> : IFoo<T>, IFoo<U> { ... } 

потому, что вы могли бы построить C<int, int> и CLR не будет иметь никакого способа знать, какие методы соответствовали каким слотам интерфейса.

Но я, кажется, несколько отвлекся. Вернемся к теме.

Поскольку вы являетесь создателем этого класса, вы можете переименовать свои методы «Бар», чтобы они были разными при любой возможной конструкции. Предположим, вы упрямо решили не делать этого. Есть ли что-нибудь, что может сделать пользователь вашего несчастного класса, если они хотят сделать Widget<int, int>? Да, на самом деле, есть, как указывает kvb. Они могут определять методы расширения, которые делают правильную вещь.

public static void BarTheFirst<A, B>(this Widget<A, B> w, A a) 
{ 
    w.Bar(a); 
} 

public static void BarTheFirst<A, B>(this Widget<A, B> w, B b) 
{ 
    w.Bar(b); 
} 

разрешение перегрузки осуществляется во время компиляции и во время компиляции все мы знаем, что первый один называет бар, который принимает «А», а второй один называет бар, который принимает " B». Мы не повторно сделать разрешение перегрузки во время выполнения, так что теперь вы можете сказать

Widget<int, int> w = whatever; 
w.BarTheFirst(5); 
w.BarTheSecond(10); 

и он будет делать правильные вещи.

+0

«На самом деле, IIRC CLR оставляет за собой право не создавать тип, создающий двусмысленность в таких подписях методов». Шутки в сторону? Если это так, не должен ли компилятор C# вызывать предупреждение (по крайней мере) при создании 'Widget '? –

+0

Ну, это так: см. OP: «ошибка компиляции двусмысленных вызовов» – user76035

+0

@wwosik: Эта ошибка для вызова метода. Эрик говорит о том, что CLR раздувает программу в то время, когда такой тип создается ** **. Фактически, когда я думаю об этом, компилятору нелегко понять, что, когда он глубоко вложен в общие методы, скажем, внешние сборки. –

5

Дайте ваши функции уникальные имена

0

Это похоже на случай, когда вы хотели бы базовый универсальный класс

abstract class WidgetBase<T1> 
{ 

    public abstract bool Bar(T1 type); 
    ... 
} 

Тогда наследуют для реализации

class WidgetA : WidgetBase<int> 
{ 
    public bool Bar(int type) 
    { 
     ... 
    } 
} 

class WidgetB : WidgetBase<int> 
{ 
    public bool Bar(int type) 
    { 
     ... 
    } 
} 
1

Я согласен с другими, что вы должны изменить свой класс, так что столкновение (например, путем переименования методов). Однако, если вы не владеете классом, там есть обходной путь:

public static class WidgetExtensions 
{ 
    public static bool Bar1<T1,T2>(this Widget<T1, T2> w, T1 t1) { return w.Bar(t1); } 
    public static bool Bar2<T1,T2>(this Widget<T1, T2> w, T2 t2) { return w.Bar(t2); } 
} 

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

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