2013-04-11 4 views
2

я это желательно иерархия классов:C# реализовать метод интерфейса с параметром типа подкласса

interface IClass 
{ 
    string print(IClass item); 
} 

class MyClass : IClass 
{ 
    // invalid interface implementation 
    // parameter type should be IClass not MyClass 
    string print(MyClass item) 
    { return item.ToString(); } 
} 

Я пытался решить проблему реализации интерфейса с помощью универсальных типов в следующем без успеха:

interface IClass 
{ 
    string print<T>(T item) where T : IClass; 
} 

class MyClass : IClass 
{ 
    string print<T>(T item) where T : MyClass 
    { return item.ToString(); } 
} 

Что мне делать?

+0

Вы должны сделать сам интерфейс универсальным и дать классу реализации желаемый тип. – Rik

+1

Как насчет того, чтобы не иметь параметр в методе печати и сделать каждый подкласс инъекцией реализации IClass в contructor или sertter. –

+1

Метод toString наследуется от Object, поэтому почему вам действительно нужно иметь 'print (элемент MyClass)', а не только 'print (Object item)'? Вы скрываете этот метод? – Thomas

ответ

11

Сделайте свой интерфейс родовое

interface IClass<T> where T : IClass<T> 
{ 
    string print(T item); 
} 

class MyClass : IClass<MyClass> 
{ 
    public string print(MyClass item) 
    { 
     return item.ToString(); 
    } 
} 
+0

Спасибо @Mehmet Ataş, вы спасли мою жизнь :) Это работает – Lio

+2

Обратите внимание, что хотя это действительно работает, (1) это сбивает с толку, и (2) оно не обязательно обеспечивает ограничение, которое вы хотите применить. См. Http://blogs.msdn.com/b/ericlippert/archive/2011/02/03/curiouser-and-curiouser.aspx для некоторых мыслей об этом шаблоне. –

+0

@ Эрик, Что вы предлагаете решить проблему более четко? – Lio

0

Вам просто нужно пройти IClass в качестве параметра к вашему методу.

interface IClass 
{ 
    string print(IClass item); 
} 

class MyClass : IClass 
{ 
    public string print(IClass item) 
    { return item.ToString(); } 
} 
+1

Мое требование заключается в том, что метод печати MyClass должен получать параметр точно MyClass, а не других подклассов IClass. – Lio

+0

На данный момент, если мы просто проигнорируем, что нужно программировать на интерфейсы, а не на реализацию. что не так со следующим интерфейсом? интерфейс IClass {строка печати (элемент MyClass); } – WPFAbsoluteNewBie

+0

Ничего. Хорошо. Но, как сказано ниже @ Эрик Липперт, это запутывает – vcRobe

7

Полезно понять, почему это незаконно. Функция, которую вы хотите, это формальный тип параметра covariance, и очень немногие языки предлагают его. (Эйфель, я думаю, это как особенность.) Он не часто встречается на языках, потому что он небезопасен! Позвольте мне проиллюстрировать пример:

class Animal {} 
class Lion : Animal { public void Roar() { } } 
class Giraffe : Animal { } 
interface IFoo { void M(Animal a); } 
class C : IFoo 
{ 
    public void M(Lion lion) { lion.Roar(); } 
} 
class P 
{ 
    public static void Main() 
    { 
     IFoo foo = new C(); 
     foo.M(new Giraffe()); 
    } 
} 

И мы просто сделали рев жирафа.

Если вы посмотрите на все эти преобразования типов, единственное, что можно разумно сделать незаконным, соответствует C.M(Giraffe) - IFoo.M(Animal).

Теперь, формальный параметр типа контрвариантность является типизированного но это не является законным в C# за исключением некоторых очень ограниченных обстоятельствах. Если C# поддерживает его, что это не так, то вы могли бы безопасно сделать что-то вроде этого:

interface IBar { void M(Giraffe g); } 
class D : IBar 
{ 
    public void M(Animal animal) { ... } 
} 
class P 
{ 
    public static void Main() 
    { 
     IBar bar = new D(); 
     bar.M(new Giraffe()); 
    } 
} 

посмотреть, что там произошло? IFoo.M говорит: «Я могу взять жирафа», а C.M говорит: «Я могу принять любого жирафа, потому что на самом деле я могу принять любого животного». Это было бы типичным, если бы C# поддерживал его, но он поддерживает его только двумя способами:

  • Контравариантный общий делегат и преобразования интерфейса.
  • Контравариантные преобразования групп методов в типы делегатов.

Примером первого является то, что выражение типа IComparable<Animal> может быть присвоено переменной типа IComparable<Giraffe> той же самой логикой: метод, который сравнивает два животных могут быть использованы, где это метод, который сравнивает два жирафа требуется , Это был добавлен в C# 4.

Примером второго является:

delegate void MyFunction(Giraffe g); 
... 
D d = new D(); 
MyFunction myfunc = d.M; 

Опять же, нам нужна функция, которая принимает жирафа, и мы поставляем один, который принимает любое животное. Эта функция была добавлена ​​в C# 2.