簡體   English   中英

C# Autofac 通用接口解析為基礎 class 實例而不是子實例

[英]C# Autofac Generic interface resolve to instance of base class instead of child

首先,我的代碼的一些背景。

我有一個可以與數據存儲/存儲庫類型服務一起保存/使用的項目的界面:

public interface IItem
{
    Guid Id { get; set; }
}
public interface IDataStore<T> where T: IItem { ... }

此類項目的示例:

public class ItemNormalExample : IItem
{
    public Guid Id { get; set; }
    public string RandomProperty { get; set; }
}

然后我還有特殊/特定類型的項目,其他項目繼承自這些項目。 這就是我的問題所在。

public interface ISpecialItem : IItem
{
    string ImportantString { get; set; }
}
public class ItemSpecialBase : ISpecialItem
{
    public Guid Id { get; set; }
    public string ImportantString  { get; set; }
}
public class ItemSpecialExample : ItemSpecialBase 
{
    public string RandomProperty { get; set; }
}

我正在使用 Autofac 來解析IItem的每個具體實現的這個數據存儲的實例。 IE:

builder.RegisterType<DataStore<ItemNormalExample>>().As<IDataStore<ItemNormalExample>>();

但是,這可以按預期工作,但是,對於從ItemSpecialBase基礎 class 繼承的對象,我希望將它們實例化為基礎 class 類型的 DataStore,而不是實際的子 ZA2F2ED4F8EBC2CBB4C21A29DZ40AB61

像這樣的東西(這不起作用):

builder.RegisterType<DataStore<ItemSpecialBase>>().As<IDataStore<ItemSpecialExample>>();

我的意圖是讓每個從 ItemSpecialBase 繼承的ItemSpecialBase都使用相同的 DataStore。 我的 DataStore class 支持這一點並且工作正常。

如何使用 Autofac 完成此任務?

看起來您遇到了Variant Generic Interface問題!

具體來說,當你...

builder
  .RegisterType<DataStore<ItemSpecialBase>>()
  .As<IDataStore<ItemSpecialExample>>();

...您正在嘗試將Storage<Base>轉換為IStorage<Child> ,這稱為逆變

逆變問題可以簡化為:

class Animal { }
class Cat : Animal { }
class Dog : Animal { }

interface IStorage<T> {
    void Put(T thing);
    T? Get();
}
class Storage<T> : IStorage<T> {
    public void Put(T t) { }
    public T? Get() { return default(T); }
}

void Main() {
    var builder = new ContainerBuilder();

    // What you're attempting to do:
    builder.RegisterType<Storage<Animal>>().As<IStorage<Cat>>();
    builder.Build();

    // Is equivalent to this:
    IStorage<Cat> cats = new Storage<Animal>();

    // The compiler will stop you from doing either.
}

問題是 generics 在compile-time被驗證。 並且編譯器注意到,雖然您可以Put Cat in Animal Storage 中,但當您嘗試Get something out of storage時,它不能保證您會得到 Cat - 該存儲可能包含 Dogs (注意inout ,它們與協變/逆變討論相關)。

您可以做的一件事是向編譯器確認您只會嘗試將 Child class in ,而不會嘗試將 Child 取出:

// Notice the `in` constraint
interface IStorage<in T> {
    // We can pass `T` in as parameter
    void Put(T thing);
    
    // We cannot return `T` anymore, because we promised to
    // only use T in the `in` direction, and not out.
    object? Get();
}

// This stays the same. `in` can only be enforced against interfaces.
class Storage<T> : IStorage<T> {
    public void Put(T t) { }
    public object? Get() { return default(T); }
}

void Main() {
    var builder = new ContainerBuilder();

    // This is now allowed:
    builder.RegisterType<Storage<Animal>>().As<IStorage<Cat>>();
    builder.Build();
    
    // And this too:
    IStorage<Cat> cats = new Storage<Animal>();
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM