2017-07-07 80 views
5

我有兩個接口:無法轉換泛型類型的對象,以通用接口C#

public interface IDbModel {} 
public interface IDmModel {} 

和類源於此:

public class DbModel : IDbModel {} 
public class DmModel : IDmModel {} 
public class Middle { } 

我也有兩個接口的限制:

public interface IRule { } 
public interface IRule<in TInput, out TOutput> : IRule 
    where TInput : IDmModel 
    where TOutput : IDbModel 
{ 
    TOutput Apply(TInput elem); 
} 

還有一個從這個接口派生的抽象類:

public abstract class Rule<TDmModel, TMiddle, TDb> : IRule<TDmModel, TDb> 
    where TDmModel : IDmModel 
    where TDb : IDbModel 
{ 
    private readonly Func<TDmModel, TMiddle> _rule; 
    protected Rule(Func<TDmModel, TMiddle> rule) { _rule = rule; } 
    protected abstract TDb Apply(TMiddle transformedMessage); 
    public TDb Apply(TDmModel elem) { ... } 
} 

在此之後,我創建這個抽象類派生的兩個類:

public class RuleA : Rule<DmModel, Middle, DbModel> 
{ 
    public RuleA(Func<DmModel, Middle> rule) : base(rule) {} 
    protected override DbMode Apply(Middle transformedMessage) { ... } 
} 

public class RuleB : RuleA 
{ 
    public RuleB() : base((dm) => new Middle()) {} 
} 

RuleB:RuleA:規則< DmModel,中東,DbModel之後>:的iRule < IDmModel,IDbModel>:iRule中

而且當我嘗試將RuleB的對象投射到IRule<IDmModel, IDbModel>時,未處理的異常

無法投射'ParsreCombinators.RuleB'類型的對象來鍵入'ParsreCombinators.IRule`2 [ParsreCombinators.IDmModel,ParsreCombinators.IDbModel]'。

var ruleB = (IRule<IDmModel, IDbModel>)new RuleB(); // Exception 
IDbModel dbModel = ruleB.Apply(new DmModel()); 

什麼錯this

爲了讓這個例子較少混亂我把它簡化:

編輯:

我明白的答案後,是什麼問題,爲了讓這個例子不那麼容易混淆,我簡化了它:

public interface IDbModel {} 
public interface IDmModel {} 

public class DbModel : IDbModel {} 
public class DmModel : IDmModel {} 

public interface IRule<in TInput, out TOutput> 
    where TInput : IDmModel 
    where TOutput : IDbModel 
{ 
    TOutput Apply(TInput elem); 
} 

public class RuleA : IRule<DmModel, DbModel> 
{ 
    public DbModel Apply(DmModel elem) { ... } 
} 

var ruleA = (IRule<IDmModel, IDbModel>)new RuleA(); // Exception 

回答

7

這是很多你到了那裏間接的水平......

這裏的問題:

public abstract class Rule<TDmModel, TMiddle, TDb> : IRule<TDmModel, TDb> 
    where TDmModel : IDmModel 
    where TDb : IDbModel 

public class RuleA : Rule<DmModel, Middle, DbMode> 
public class RuleB : RuleA 
... 
var ruleB = (IRule<IDmModel, IDbModel>)new RuleB(); 

RuleB實現的iRule < DmModel,DbMode >

這不能轉換到的iRule < IDmModel,IDbModel >。 C#不支持這種類型的轉換。出於同樣的原因,你不能這樣做List<object> b = (List<object>)new List<string>();(給出「無法將類型「System.Collections.Generic.List <串>到System.Collections.Generic.List <對象>。」)

這是協方差問題。

下面是來自微軟一些關於這個問題的更多信息:https://docs.microsoft.com/en-us/dotnet/standard/generics/covariance-and-contravariance

+1

@kogoia我做了更多的挖掘,並且注意到你分別聲明瞭你的接口參數爲'in'和'out'。這意味着你實際上可以這樣做:'var ruleB =(IRule )new RuleB();'DmModel在這種情況下必須是具體的,但是IDbModel可以作爲接口保留在cast中。這是否有助於你想要做什麼? –

+0

不是,我想得到如上所示的結果'(IRule )new RuleB()' – kogoia

+0

'public RuleB():base((dm)=> new Middle()){}'我想在這裏'(dm)=>新中間(dm.SomeProperty)'中'dm'是'DmModel','SomeProperti'是DmModel類的屬性,它不在接口聲明中,不能是 – kogoia

3

這是一個非常令人困惑的例子,但我相信問題在於你正在轉換爲泛型類型併爲其提供接口,當你派生的類迫使你使用DmModel和DbMode時。

也許這就是你的意思:

var ruleB = (IRule<DmModel, DbMode>)new RuleB(); 

,編譯就好了,我用這種方式你的類是結構化的,比其他重組是唯一的選擇。

+0

是的,它編譯和工作,但我不知道當我做鑄造時這個類的類型。我的意思是'DmModel'和'DbModel' – kogoia

+1

OP的問題不是編譯,而是一個運行時異常。但是,這個建議似乎解決了這個問題。 – CoderDennis

3

你的界面IRule<in TInput, out TOutput>既是協逆變,這意味着你不能協變投它。逆變防止了這一點。

基本上,您的任務var dbModel = (IRule<IDmModel, IDbModel>)new RuleB();斷言dbModel必須接受任何IDmModel參數。不幸的是,這不是事實;由於RuleA的具體形式,實例必須可分配到DmModel,因此IDmModel的其他衍生產品將失敗。

+0

這是一個很好的解釋,爲什麼這種類型的鑄造是不允許的。 –

相關問題