2011-11-11 27 views
8

我正在寫一個通用的方法在T4模板的特殊任務中使用它。該方法應該允許我使用通用接口中的專用類型。我想到了以下特徵:一個通用的方法可以使用逆變/協變類型?

interface IGreatInterface { 
    Object aMethodAlpha<U>(U parameter) where U : IAnInterface; 
    Object aMethodBeta(IAnInterface parameter) 
} 

public class AnInterestingClass : IAnInterface{} 

當我嘗試實施IGreatInterface編譯器標誌爲aMethodBeta()一個錯誤,因爲我做了我的T4編寫使用的IAnInterface一種亞型(也就是我想要實現這個方法方法是這樣的:Object aMethodBeta(AnInterestingClass parameter))。

方法aMethodAlpha<U>()可以使用,但不像我想要的那樣乾淨,因爲我的T4必須生成一些額外的代碼。我(可能錯了) 提出該方法的實施必須由T4完成,可能是
Object aMethodAlpha<AnInterestingClass>(AnInterestingClass parameter)

我在想通用方法不支持逆變類型,但我不確定;我想這是編譯器阻止編碼器使用具有通用類型中未定義方法的特定類型的方式...

  1. 通用方法在實施時是否必須使用確切類型?
  2. 是否有任何技巧可以改變這種行爲?
+0

我不太明白你的問題。你能發佈實現IGreatInterface的代碼和特定的編譯器錯誤嗎? – phoog

+2

@Juan:作爲一個方面說明它有助於用*更新*標記您的更新或什麼,所以我們看到了什麼改變。 –

回答

20

這個問題是相當混亂。讓我看看我能否澄清它。

當我嘗試實施IGreatInterface編譯器標誌的錯誤aMethodBeta(),因爲我做了使用亞型的IAnInterface我想實現像這樣的方法,該方法:Object aMethodBeta(AnInterestingClass parameter)

這不合法。有些簡化:

class Food {} 
class Fruit : Food {} 
class Meat : Food {} 
interface IEater 
{ 
    void Eat(Food food); 
} 
class Vegetarian : IEater 
{ 
    public void Eat(Fruit fruit); 
} 

Vegetarian不履行的IEater合同。您應該可以通過任何食物吃,但Vegetarian只接受水果。 C#不支持虛擬方法形式參數協方差,因爲它不是類型安全的。

現在,你可能會然後說,這個怎麼樣:

interface IFruitEater 
{ 
    void Eat(Fruit fruit); 
} 
class Omnivore : IFruitEater 
{ 
    public void Eat(Food food); 
} 

現在我們已經得到了類型安全; Omnivore可用作IFruitEater,因爲Omnivore可以吃水果以及任何其他食物。

不幸的是,C#不支持虛擬方法形式參數類型衝突即使這樣做理論上是類型安全的。很少有語言支持這一點。

同樣,C#不支持虛擬方法返回類型方差要麼。

我不確定這是否真的回答了您的問題。你能澄清這個問題嗎?

UPDATE:

關於什麼:

interface IEater 
{ 
    void Eat<T>(T t) where T : Food; 
} 
class Vegetarian : IEater 
{ 
    // I only want to eat fruit! 
    public void Eat<Fruit>(Fruit food) { } 
} 

不,這不是法律無論是。 IEater的合同是你會提供一種方法Eat<T>,可以採取任何TFood。你不能部分實行承包,比你能做到這一點任何更多:

interface IAdder 
{ 
    int Add(int x, int y); 
} 
class Adder : IAdder 
{ 
    // I only know how to add two! 
    public int Add(2, int y){ ... } 
} 

但是,你可以這樣做:

interface IEater<T> where T : Food 
{ 
    void Eat(T t); 
} 
class Vegetarian : IEater<Fruit> 
{ 
    public void Eat(Fruit fruit) { } 
} 

這是完全合法的。但是,您不能這樣做:

interface IEater<T> where T : Food 
{ 
    void Eat(T t); 
} 
class Omnivore : IEater<Fruit> 
{ 
    public void Eat(Food food) { } 
} 

因爲C#不再支持虛擬方法形式參數逆變或協方差。

注意,C#確實支持參數多態性協方差這樣做時被稱爲是類型安全。例如,這是合法的:

IEnumerable<Fruit> fruit = whatever; 
IEnumerable<Food> food = fruit; 

水果序列可以用作一系列食物。或者,

IEnumerable<Fruit> fruitComparer = whatever; 
IComparable<Apples> appleComparer = fruitComparer; 

如果你有什麼東西可以比較任何兩個水果,那麼它可以比較任意兩個蘋果。 (1)方差可證明是類型安全的;(2)類型的作者增加了方差註釋,表示期望的合作和對照;(2) (3)涉及的變量類型參數都是引用類型,(4)泛型類型是委託或接口。

+0

(很好的答案!這個評論將用於澄清問題後面...)。使用你的例子:我可以創建一個簽名'IEater.Eat (T food)T:Food'然後執行'Omnivore.Eat (水果食物)'?沒有編譯器標記我沒有實現IEater。吃(T食物)'?.從你的答案我認爲這是不可能的... – JPCF

+0

@JuanPabloContreras:我已經添加了一些關於你的評論的文字。 –

+0

@EricLippert如果可能,請給你對這個問題的想法http://stackoverflow.com/questions/8109478/process-address-space-vs-virtual-memory – Sandeep

2

如果您想從通用接口繼承,請參閱phoog的答案。如果您正在討論試圖實現一個界面的共變化,這導致我下面的討論。

假設:

internal interface IAnInterface { } 

public class SomeSubClass : IAnInterface { } 

public class AnotherSubClass : IAnInterface { } 

public GreatClass : IGreatInterface { ... } 

,試圖實現與多個派生(合變體)的界面的說法是沒有的時候,這是通過一個IAnInterface傳遞將是一個接口稱爲保函的問題SomeSubClass實例。這就是爲什麼它直接允許而不是

IGreatInterface x = new GreatClass(); 

x.aMethodBeta(new AnotherSubClass()); 

IF你可以做協方差,這將失敗,因爲你會期待一個SomeSubClass,但會得到一個AnotherSubClass

什麼你可能做的是做顯式接口實現:

class GreatInterface : IGreatInterface 
{ 
    // explicitly implement aMethodBeta() when called from interface reference 
    object IGreatInterface.aMethodBeta(IAnInterface parameter) 
    { 
     // do whatever you'd do on IAnInterface itself... 
     var newParam = parameter as SomeSubClass; 

     if (newParam != null) 
     { 
      aMethodBeta(newParam); 
     } 

     // otherwise do some other action... 
    } 

    // This version is visible from the class reference itself and has the 
    // sub-class parameter 
    public object aMethodBeta(SomeSubClass parameter) 
    { 
     // do whatever 
    } 
} 

因此,如果你這樣做,你的接口支持通用的,班裏有一個更具體的方法,但仍然支持接口。主要的區別是你需要處理,突如其來的實施IAnInterface在傳遞的情況下

UPDATE:這聽起來像你想是這樣的:

public interface ISomeInterface 
{ 
    void SomeMethod<A>(A someArgument); 
} 

public class SomeClass : ISomeInterface 
{ 
    public void SomeMethod<TA>(TA someArgument) where TA : SomeClass 
    { 

    } 
} 

這是不允許的,當你從一個接口實現一個通用方法時,約束必須匹配。

+0

嗯......從你的答案我可以推斷.net不接受具有通用方法的子類型...我是否正確? – JPCF

+0

@Juan:我不確定你的意思是「不接受」。你的意思是作爲論據嗎?作爲參數類型? –

+0

@Juan:如果你的意思是我可以實現一個接口,但使類型更接近於接口類型?不,你不能。您可以共同變化,反向傳遞/返回參數,並且您可以共同變化地反向分配接口和委託(如果標記得當),但不是實現。 –

0

也許您正在尋找這樣的:

interface IGreatInterface<in U> where U : IAnInterface 
{ 
    Object aMethodAlpha(U parameter); 
} 

class SomeClass : IAnInterface { /*...*/ } 

class GreatClass : IGreatInterface<SomeClass> 
{ 
    public Object aMethodAlpha(SomeClass parameter) {} 
} 

編輯:

是的,你是對的:如果你在一個接口定義了一個通用的方法,你可以不執行與該方法使用兼容型的具體方法。

如何使用委託(因爲代表支持合作和逆變):

[例如刪除,因爲我得到了方差倒退 - 這是行不通的。]

+0

不...請閱讀更新... – JPCF

相關問題