2013-01-11 22 views
4

我一直在試圖將一些使用(有界)通配符泛型的Java代碼轉換爲C#。 我的問題是,Java似乎允許通用類型與通配符一起使用時是協變和逆變。例如:.NET等效於Java通配符泛型<?>具有同向和反向變化?

的Java:

interface IInterf { } 

class Impl implements IInterf { } 

interface IGeneric1<T extends Impl> { 
    void method1(IGeneric2<?> val); 
    void method1WithParam(T val); 
} 

interface IGeneric2<T extends Impl> { 
    void method2(IGeneric1<?> val); 
} 

abstract class Generic<T extends Impl> implements IGeneric1<T>, IGeneric2<T> { 
    public void method1(IGeneric2<?> val2) { 
     val2.method2(this); 
    } 
} 

...作品。 (?)

C#相當於

interface IInterf { } 

class Impl : IInterf { } 

interface IGeneric1<T> where T:Impl { 
    //Java was: 
    //void method1(IGeneric2<?> val2); 
    void method1(IGeneric2<Impl> val); 
    void method1WithParam(T to); 
} 

interface IGeneric2<T>where T:Impl { 
    void method2(IGeneric1<Impl> val); 
} 

abstract class Generic<T> : IGeneric1<T>, IGeneric2<T> where T : Impl 
{ 
    //Java was: 
    //public void method1(IGeneric2<?> val2) { 
    public void method1(IGeneric2<Impl> val2) 
    { 
     val2.method2(this); //'this': Argument type 'Generic<T>' is not 
          //assignable to parameter type 'IGeneric1<Impl>' 
    } 

    public abstract void method1WithParam(T to); 
    public abstract void method2(IGeneric1<Impl> val); 
} 

...無法編譯 - 看到錯誤的註釋。這是可以預料到的,因爲IGeneric的泛型參數沒有標記爲協方差的「out」。

如果我改變了:

interface IGeneric1<T> where T:Impl { 

這個

interface IGeneric1<out T> where T:Impl 

錯誤消失,但一個又一個的出現,爲方法的聲明採用相同的接口內的通用參數:

interface IGeneric1<T> where T:Impl { 
    void method1WithParam(T val); //Parameter must be input-safe. 
         //Invalid variance: The type parameter 'T' must be 
         //contravariantly valid on 'IGeneric1<out T>'. 

建議?

[另見follow-up question了稍硬的場景]

+0

什麼是Java中的尖括號內的問號是什麼意思? –

+0

「任何類型都行」(或者在這種情況下,任何類型都來自'Impl')。它被稱爲「通配符」。 http://docs.oracle.com/javase/tutorial/extra/generics/wildcards。html –

回答

5

您需要翻譯的Java通配符通用方法C#中的方法是在自己的權利通用。例如,這樣的:

interface IGeneric2<T extends Impl> { 
    void method2(IGeneric1<?> val); 
} 

應轉換爲

interface IGeneric2<T>where T:Impl { 
    void method2<U>(IGeneric1<U> val) where U:Impl; 
} 

是必要的重複類型約束爲通過IGeneric1<T>指定爲U類型約束T

這樣做的原因是,在Java版本有method1method2參數的類型參數隱含約束:如果該參數必須是某種IGeneric1<X>然後X顯然必須是Impl,否則它不可能爲該類型實施IGeneric1

在C#中約束條件必須是明確的,因此您需要重複IGeneric1<T>IGeneric2<T>要求的T

所以等效代碼爲:

interface IInterf { } 

class Impl : IInterf { } 

interface IGeneric1<T> where T:Impl { 
    void method1<U>(IGeneric2<U> val) where U:Impl; 
    void method1WithParam(T to); 
} 

interface IGeneric2<T>where T:Impl { 
    void method2<U>(IGeneric1<U> val) where U:Impl; 
} 

abstract class Generic<T> : IGeneric1<T>, IGeneric2<T> where T : Impl 
{ 
    public void method1<U>(IGeneric2<U> val2) where U:Impl 
    { 
     val2.method2(this); 
    } 

    public abstract void method1WithParam(T to); 
    public abstract void method2<U>(IGeneric1<U> val) where U:Impl; 
} 
+0

'method2'的Java版本實際上是有界的嗎?根據OP發佈的文檔,有界通配符聲明約束內聯。在OP的Java代碼中沒有這樣的約束。我假設用'IGeneric1 '調用'method2'是有效的。 –

+0

@DanielHilgarth:「IGeneric1 」和「IGeneric2 」要求'T'擴展Impl有一個隱含約束。 'method1'和'method2'說'我會採用任何'IGenericX'',但實際上* * *型的實現'IGenericX'不能用於泛型類型參數的任何*值。除非我非常錯誤,因爲我的Java是低標準的。 – Jon

+0

當然,你是正確的 - 這就是約束來自何處。在C#中,你必須明確地「重複」對方法的約束,而在Java中,這是隱含的。謝謝。 –