[英]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 。 (注意in
和out
,它們與協變/逆變討論相關)。
您可以做的一件事是向編譯器確認您只會嘗試將 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.