2012-05-30 3 views
10

Я работаю с кодом, который содержит следующий перегруженный метод в обобщенном классе:метод перегрузки в обобщенном классе

public class A<T> 
{ 
    public void Process(T item) { /*impl*/ } 
    public void Process(string item) { /*impl*/ } 
} 

Когда параметризующее класс для string я потерять возможность вызвать версию с родовым параметром?

var a = new A<string>(); 
a.Process(""); //Always calls the non-generic Process(string) 

ответ

5

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

public static CallerClass 
{ 
    public static CallGenericOverload<T>(GenericClass<T> cls, T val) 
    { 
     return cls.ProblemOverload(val); 
    } 

    //We can also make an extension method. 
    //We don't have to of course, it's just more comfortable this way. 
    public static CallGenericOverloadExtension<T>(this GenericClass<T> cls, T val) 
    { 
     return cls.ProblemOverload(val); 
    } 

} 

public GenericClass<T> 
{ 
    public string ProblemOverload(T val) 
    { 
     return "ProblemOverload(T val)"; 
    } 
    public string ProblemOverload(string val) 
    { 
     return "ProblemOverload(string val)"; 
    } 
} 

Теперь, если мы делаем следующее:

var genClass = new GenericClass<string>(); 
Console.WriteLine(genClass.ProblemOverload("")); //output: ProblemOverload(string val) 
Console.WriteLine(CallerClass.CallGenericOverload(genClass, "")); //output: ProblemOverload(T val) 
Console.WriteLine(genClass.CallGenericOverloadExtension("")); //output: ProblemOverload(T val) 

Вы можете использовать подобный трюк, если вы определили общие класса вместо общего метода. Важно то, что параметр, который вы передаете в ProblemOverload, должен иметь тип T, а не тип string в вызове. В конце концов, метод CallGenericOverload знает, что он получает T во время сборки, поэтому он будет привязан к перегрузке, которая принимает параметр. Не имеет значения, что на самом деле он будет получать string во время выполнения.

+0

Странно, но прекрасно в то же время! – Jay

+0

Это должен был быть принятый ответ! – Jay

14

Конкретные типы имеют преимущество перед родовыми типами.

Например, это то, что я тестировал в LINQPad.

void Main() 
{ 
    new A<string>().Process("Hello"); 
} 

public class A<T> 
{ 
    public void Process(T item) { Console.WriteLine("T"); } 
    public void Process(string item) { Console.WriteLine("string"); } 
} 

// Output: string 

Если у вас есть проблема со скрытием общего метода, вам нужно что-то передумать. Перегружая общий метод конкретными типами, вы фактически говорите: «Используйте общую перегрузку, если вам нужно, но если можете, используйте конкретную версию, , потому что она должна знать, что лучше.«

+0

Итак, я понимаю, что да? – mellamokb

+0

Да. Ответ: «Да». –

+0

См. Также: http://blogs.msdn.com/b/ericlippert/archive/2009/07/30/generics-are-not-templates.aspx –

0

Имея делал это раньше, я склонен сказать «Нет», но всегда есть люди с более узнаваемыми людьми, которые будут спорить иначе.

Если память используется, то компилятор runtime выбирает наиболее строго типизированную перегрузку для выполнения.

РАЗЪЯСНЕНИЕ

Мой ответ плохо сформулирован, и я заслуживаю downvote. спросил

ОП, «Когда параметризующее класс для строки я теряю возможность вызвать версию с общим параметром?» Я не отвечал, что «Нет, вы не можете сделать это», но «Нет, вы не теряете возможность вызывать версию с общим параметром».

Я должен был быть более ясным.

+2

Собственно, это разрешено во время компиляции. Время выполнения ничего не выбирает. –

+0

Хорошо сказано. Я подумал, что это так, но это то, что я получаю для публикации, находясь под инфарктом сиропа от кашля. : -/ –

+0

Hm, re. ваше разъяснение: на самом деле вы потеряете возможность вызывать версию te с общим параметром (если параметр имеет строку типа, что и запрашивает OP). – jeroenh

5

Да. Это описано в спецификации C#, раздел 7.5.3, разрешение перегрузки.

От 7.5.3.6:

«В то время как подписи объявлено должны быть уникальными, то возможно, что замена аргументов результатов типа в идентичных подписях в таких случаях врезки нарушения правил перегрузки. разрешение выше будет выбрать наиболее конкретного участника. "

Пример, приведенный там говорится, что в приведенном ниже случае, разрешение перегрузки для G<int>.F1 подберут необщего

class G1<U> 
{ 
    int F1(U u); 
    int F1(int i); 
} 

Галстук отключающая правило, которое применяется здесь описано в 7.5.3.2, "Лучше член функции":

В случае последовательности типа параметров {P1, P2, ..., PN} и {Q1, Q2, ..., QN} эквивалентны (т.е.каждый Pi имеет преобразование идентичности в , соответствующее Qi), в отношении порядка применяются следующие правила связывания, чтобы определить лучший член функции.

  • Если МП не является шаблонным методом и MQ является универсальным методом, то MP лучше, чем MQ.
+0

Как и ответ, не нравится правило ... но, к счастью, вы можете вызвать метод явно – Jay

+0

@Jay: Нет, вы не можете ... –

-2

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

public class A<T> 
{ 

    public void Process<t>(t item) 
    { 

     if (typeof(t) == typeof(string)) 
     { 
      this.Process(item.ToString()); 
      return; 
     } 

     /*impl*/ 

    } 

    public void Process(string item) { /*impl*/ } 
} 

public class AString 
    : A<String> 
{ 
    public new void Process<T>(T item) 
    { 
     base.Process<T>(item); 
    } 
} 

Оба способа работают ..

 var x = new A<String>(); 
     x.Process(""); 
     x.Process<String>(""); 

     var y = new AString(); 
     y.Process(""); 
     y.Process<String>(""); 

Лично я бы переместите уродливый тип проверки на Derived и обработайте его там ...

Выходы: (Если вы замените /*impl*/ на Console.WriteLine)

From Non Generic 
From Non Generic 
From Non Generic 
From Non Generic 

Вы также можете вызвать пометку метода private, а затем выставить только общий. С внешней стороны метод будет вызван с Reflection, если необходимо.

+0

Возможно, вам стоит добавить дополнительные детали. Особенно пример кода. И почему у вас есть два ответа? –

+0

Другой был удален ... потому что я не был высоким .. и так как я пример кода будет следовать: P – Jay

+0

Он не выглядит удаленным для меня. –

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