我想使用 C# 8 默认接口实现来面对代码中的性能问题。
实际上,我有这个特点:
public interface IDataAdapter {}
public interface IDataAdapter<T> : IDataAdapter
{
void Insert(T value);
}
我实际上必须在所有IDataAdapter
中进行反射,检查泛型类型并通过特定 T 实例的反射调用Insert
。我想做的是:
public interface IDataAdapter
{
void InsertValue(object value);
}
public interface IDataAdapter<T> : IDataAdapter
{
void Insert(T value);
public void InsertValue(object value) => Insert(value as T);
}
编译器说使用关键字 new 来屏蔽继承的方法。但是,我唯一要完成的是已经实现了一个非泛型方法,以使所有IDataAdapter<T>
实现只需要实现泛型版本。
这是我能完成的还是仍然不可能完成的?我已经知道使用抽象类是解决此问题的一种方法,但我想允许开发人员拥有一个实现许多 IDataAdapter 的类......
这是我当前的反射代码:
public IEnumerable<IDataAdapter> DataAdapters { get; }
public Repository(IEnumerable<IDataAdapter> dataAdapters)
{
DataAdapters = dataAdapters;
}
public async Task SaveAsync()
{
foreach (var item in aggregates)
{
foreach (var dataAdapter in DataAdapters)
{
if (dataAdapter.GetType().GetInterfaces().Any(i => i.IsGenericType && i.GetGenericArguments()[0] == item.GetType()))
{
dataAdapter.GetType().GetMethod("Insert", new[] { item.GetType() }).Invoke(dataAdapter, new[] { item });
}
}
}
}
从面向对象的角度来看,你想做的事情是无法完成的。
假设您创建以下类层次结构:
public interface IFoo{}
public interface IBar{}
public class A: IFoo{}
public class B: IFoo{}
public class C:IFoo,IBar {}
然后是以下适配器:
public class TestA : IDataAdapter<A>{}
public class TestB : IDataAdapter<B>{}
public class TestC : IDataAdapter<C>{}
public class TestIFoo : IDataAdapter<IFoo>{}
public class TestIBar : IDataAdapter<IBar>{}
public class TestIBoth : IDataAdapter<IFoo>,IDataAdapter<IBar>{}
如果 TestA 收到 A 的实例,会发生什么非常容易。但是TestIFoo收到C呢?目前,您的反射代码不起作用,因为您测试类型相等性(C 等于 IFoo 吗?不!即使 C 作为 IFoo 也可以(。 这打破了利斯科夫替代原则。如果某些东西适用于一个类,那么它也应该适用于它的任何子类。
假设您修复了上述观点。现在TestIBoth收到C呢?其中有两种不同的插入实现吗?当然,这是继承所必需的!但是...你必须插入 C 两次吗?还是必须在第一个拟合方法中插入一次?
你必须经历反思的原因是,所有这些问题都需要一个算法的答案。您的编译器将无法回答(顺便说一下,这使得语言阻止了它(
最后,我强烈建议使用一个非常不同的解决方案(如Wim Coenen提出的解决方案(。
我认识到这个问题,您需要查找知道如何处理某种类型项目的 IDataAdapter 实现。我已经为"视图插件"系统做了类似的事情,我会在其中寻找知道如何呈现某种类型的视图插件。如果您无法提前知道需要渲染哪种类型的对象,这将非常有用。
据我所知,试图将更多的编译时类型安全性硬塞到这种模式中是行不通的,或者如果它确实有效,那么它实际上不会提供任何好处。我只会像这样声明 IDataAdapter:
public interface IDataAdapter
{
void InsertValue(object value);
Type SupportedType { get; }
}
如果数据适配器支持多种类型,则可以改为将其IEnumerable<Type> SupportedTypes
,或者用bool SupportsType(Type)
方法替换该属性。