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 提出的解决方案)