C#8默认接口实现与继承
C# 8 default interface implementation and inheritance
我想使用 C# 8 默认接口实现来解决我的代码中的性能问题。
其实我有这个接口:
public interface IDataAdapter {}
public interface IDataAdapter<T> : IDataAdapter
{
void Insert(T value);
}
我实际上必须对所有 IDataAdapter
进行反射,检查泛型类型并通过反射调用 Insert
特定 T 实例。我想做的是:
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>
实现只需要实现通用版本。
这是我可以完成还是仍然不可能的事情?我已经知道使用抽象 class 是解决此问题的一种方法,但我想让开发人员拥有一个 class 来实现许多 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 });
}
}
}
}
我发现了这个问题,您需要查找知道如何处理特定类型项目的 IDataAdapter 实现。我为 "view plugin" 系统做了类似的事情,我会在其中寻找知道如何呈现特定类型的视图插件。如果您无法提前知道需要渲染的对象类型,这将非常有用。
据我所知,试图将更多的编译时类型安全硬塞进这个模式中是行不通的,或者即使行得通,也不会带来任何好处。我会像这样声明 IDataAdapter:
public interface IDataAdapter
{
void InsertValue(object value);
Type SupportedType { get; }
}
如果数据适配器支持多种类型,您可以改为 IEnumerable<Type> SupportedTypes
,或者用 bool SupportsType(Type)
方法替换 属性。
从面向对象的角度来看,你想做的事是做不到的。
假设您创建以下 class 层次结构:
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 as IFoo 没问题)。
这打破了 Liskov 替换原则。如果某些东西适用于 class 那么它也应该适用于它的任何子 classes.
假设您修复了上述问题。现在 TestIBoth 获得 C 怎么样? Insert 有两种不同的实现吗?当然,这是继承所需要的!但是然后......你必须插入两次C吗?还是第一种拟合方法只插入一次?
你之所以要反思,是因为所有这些问题都需要一个算法答案。你的编译器将无法回答(这使得语言顺便阻止它)
最后我强烈建议使用一种非常不同的解决方案(比如 Wim Coenen 提出的解决方案)
我想使用 C# 8 默认接口实现来解决我的代码中的性能问题。
其实我有这个接口:
public interface IDataAdapter {}
public interface IDataAdapter<T> : IDataAdapter
{
void Insert(T value);
}
我实际上必须对所有 IDataAdapter
进行反射,检查泛型类型并通过反射调用 Insert
特定 T 实例。我想做的是:
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>
实现只需要实现通用版本。
这是我可以完成还是仍然不可能的事情?我已经知道使用抽象 class 是解决此问题的一种方法,但我想让开发人员拥有一个 class 来实现许多 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 });
}
}
}
}
我发现了这个问题,您需要查找知道如何处理特定类型项目的 IDataAdapter 实现。我为 "view plugin" 系统做了类似的事情,我会在其中寻找知道如何呈现特定类型的视图插件。如果您无法提前知道需要渲染的对象类型,这将非常有用。
据我所知,试图将更多的编译时类型安全硬塞进这个模式中是行不通的,或者即使行得通,也不会带来任何好处。我会像这样声明 IDataAdapter:
public interface IDataAdapter
{
void InsertValue(object value);
Type SupportedType { get; }
}
如果数据适配器支持多种类型,您可以改为 IEnumerable<Type> SupportedTypes
,或者用 bool SupportsType(Type)
方法替换 属性。
从面向对象的角度来看,你想做的事是做不到的。
假设您创建以下 class 层次结构:
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 as IFoo 没问题)。 这打破了 Liskov 替换原则。如果某些东西适用于 class 那么它也应该适用于它的任何子 classes.
假设您修复了上述问题。现在 TestIBoth 获得 C 怎么样? Insert 有两种不同的实现吗?当然,这是继承所需要的!但是然后......你必须插入两次C吗?还是第一种拟合方法只插入一次?
你之所以要反思,是因为所有这些问题都需要一个算法答案。你的编译器将无法回答(这使得语言顺便阻止它)
最后我强烈建议使用一种非常不同的解决方案(比如 Wim Coenen 提出的解决方案)