2015-05-25 61 views
1

泛型的很多我的問題,我希望它仍然是可以理解的......通用接口,通用接口與泛型

我有以下接口:

public interface ICommandBus 
{ 
    TResult Publish<TCommand, TResult>(TCommand command) 
     where TResult : ICommandResult 
     where TCommand : ICommand<TResult>; 
} 

public interface ICommand<T> 
    where T : ICommandResult 
{ } 

public interface ICommandResult 
{ 
    bool Success { get; } 
} 

,我想用它們的實現這種方式(我ommit的CommandBus,它不是有用的這個問題,但你可以找到它here):我的錯誤

public class CreateItemCommand: ICommand<CreateItemResult> 
{ 
    public string Name { get; private set; } 
} 
public class CreateItemResult: ICommandResult 
{ 
    public bool Success { get; private set; } 
} 

var command = new CreateItemCommand(); 
var result = commandBus.Publish(command); //Error here 

The type arguments cannot be inferred from the usage。 我試過的東西與inout修飾符沒有成功...

我怎麼能說我的Publish方法,wihout指定的參數類型是這樣的:

var result = commandBus.Publish<CreateItemCommand, CreateItemResult>(command); //Works, but ugly... 

編輯:更新盧卡斯回答

盧卡斯解決方案應該工作,但我仍然必須把where TResult : ICommandResult約束,以避免錯誤There is no boxing conversion or type parameter conversion from 'TResult' to 'Core.Command.ICommandResult'

我也有繼承一個巨大的問題,比方說,我有以下的抽象CommandObserver:

public abstract class NotificationObserver<TResult> : ICommandObserver<TResult> 
    where TResult : ICommandResult 
{ 
    protected virtual bool IsEnable(ICommand<TResult> command, TResult result) 
    { 
     return true; 
    } 
} 

而且它的實現

public class CreateItemObserver : NotificationObserver<CreateItemResult> 
{ 
    protected override bool IsEnable(CreateItemCommand command, CreateItemResult result) 
    { 
     return !String.IsNullOrEmpty(command.Name); 
    } 
} 

這是行不通的,因爲實現不確實會覆蓋虛擬方法(不同的簽名:ICommand<TResult>!= CreateItemCommand)。

如果我去做保持正確的簽名,我不能沒有一個醜陋投在實現中使用CreateItemCommand屬性...

+1

所有泛型的要點是什麼?似乎反作用有泛型和專業化的一些接口... –

+0

我認爲這是重點...我濫用泛型,現在我卡... – jbuiss0n

+0

在編輯中的後續問題是它自己的問題,所以你應該單獨張貼它,如果你想它的答案。你試圖在這裏做一個雙重調度,也許你應該試試[訪問者模式](http://en.wikipedia.org/wiki/Visitor_pattern)。 – Falanwe

回答

1

我看到了仿製藥過量這裏;)太多的仿製藥,其中簡單的繼承會給你更容易的結果。

如果你想在不重構的情況下完全使用你的類來擺脫太多的泛型:你只需要指定泛型函數調用,因爲編譯器不夠智能,無法爲你做這件事(自動解析泛型參數是好的,解決泛型參數的泛型參數不是)。

var result = commandBus.Publish<CreateItemCommand, CreateItemResult>(command); 

它很沉重,它不是特別優雅,但至少它工作。在一個側面說明中,你的接口聲明本身很重要,所以它也不是太令人驚訝,也很重要。

+0

這是我擔心的,因爲代碼非常複雜,使用必須非常明確。當我看到這樣的代碼時,我習慣於想到難聞的氣味,但是在這裏我找不到任何好的重構。如果你認爲我可以避免所有這些複雜的泛型,我非常想念你的想法! – jbuiss0n

4

您可以輕鬆地重寫接口這樣擺脫TCommand參數:

public interface ICommandBus 
{ 
    TResult Publish<TResult>(ICommand<TResult> command) 
     where TResult : ICommandResult; 
} 

請注意,您也可以刪除where TResult : ICommandResult,因爲這個約束是多餘的定義在ICommand<TResult>

public interface ICommandBus 
{ 
    TResult Publish<TResult>(ICommand<TResult> command); 
} 

我猜爲什麼C#拒絕推斷出你的情況的類型參數的原因是,TCommand可以實現ICommand<T>多次,像這樣:

public class SomeCommand : ICommand<Foo>, ICommand<Bar> 
{ 
    // ... 
} 
+0

的確,您可以刪除'where TResult:ICommandResult'約束,但我會保留它。這是多餘的,是的,但是無論你做什麼都會受到約束,所以明確地寫下來會使代碼更易於閱讀和理解。有時冗餘很好。 – Falanwe

+0

由於'沒有將裝箱轉換或從'TResult'類型參數轉換爲'Core.Command.ICommandResult'',無法刪除此約束。這個錯誤出現在'Publish'方法中。我認爲在這種情況下,我應該使用'in'或'out'修飾符,但我不太瞭解它們... – jbuiss0n

+0

@ jbuiss0n您可以保持原樣,或者如果您知道TResult是一個你可以使用'class'約束的類,或者如果你想擺脫這裏的約束,你可以像這樣寫一個double cast:'(TResult)(object)command'來繞過錯誤。 –