【问题标题】:C# 8 default interface implementation and inheritanceC# 8 默认接口实现和继承
【发布时间】:2019-08-22 09:50:51
【问题描述】:

我想使用 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&lt;T&gt; 实现只需要实现泛型版本。

这是我可以完成的事情还是仍然不可能?我已经知道使用抽象类是解决这个问题的一种方法,但我想让开发人员拥有一个实现许多 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 的类”。如果开发人员这样做,然后调用非泛型 InsertValue,非泛型会选择什么类型的 T?
  • "但是我想让一个开发者有一个实现很多IDataAdapter的类……"如果一个类实现了IDataAdapter&lt;Foo&gt;IDataAdapter&lt;Bar&gt;,这两种方法中的哪一种将被InsertValue 调用?如您所见,这是不可能的。
  • InsertValue 完全是“Tagnostic”,因为它作为文档存储在后面,这就是为什么 object 是非通用选项的有效选项

标签: c# inheritance interface default-interface-member


【解决方案1】:

从面向对象的角度来看,你试图做的事情是不可能完成的。

假设您创建了以下类层次结构:

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 也可以)。 这打破了 Liskov 替换原则。如果某些东西适用于一个类,那么它也应该适用于它的任何子类。

假设您解决了上述问题。现在 TestIBoth 收到一个 C 怎么办?里面有两种不同的 Insert 实现吗?当然,这是继承要求的!但是……你必须插入 C 两次吗?还是第一种拟合方法只需要插入一次?

之所以必须进行反思,是因为所有这些问题都需要一个算法答案。您的编译器将无法回答(这使得语言顺便阻止了它)

最后,我强烈建议使用非常不同的解决方案(如 Wim Coenen 提出的解决方案)

【讨论】:

  • 附带说明,我应该指定反射代码是 POC,而不是生产就绪代码。但事实上,你是对的,即使我从你的答案中删除接口(意思是C : A, B),这也会中断。我唯一的解决方案是每个 T 都有抽象类。但是很好的解释
【解决方案2】:

我认识到这个问题,您需要查找知道如何处理特定类型项目的 IDataAdapter 实现。我为“视图插件”系统做了类似的事情,我会在其中寻找知道如何呈现某种类型的视图插件。如果您无法提前知道需要渲染什么类型的对象,这将非常有用。

据我所知,试图将更多的编译时类型安全性硬塞到这种模式中并不会真正起作用,或者如果这样做了,那么它实际上不会提供任何好处。我会像这样声明 IDataAdapter:

public interface IDataAdapter 
{
   void InsertValue(object value);
   Type SupportedType { get; }
}

如果数据适配器支持多种类型,您可以改为IEnumerable&lt;Type&gt; SupportedTypes,或者用bool SupportsType(Type) 方法替换属性。

【讨论】:

  • 这确实是可行的替代方案,但它不会为开发人员提供使用泛型编辑 C# 允许的花哨代码。他们必须转换 value 才能拥有它,如果它是一个可行的替代方案,这是我不想去的,因为其他代码段寻找通过它们的泛型类型参数检索 DataApter...
  • 再次考虑您自己的首选解决方案,我不确定您通过InsertValue(object value); 方法解决了哪个问题。如果您无论如何都需要编写使用反射来选择正确的 IDataAdapter 实现的代码,那么该代码可以直接调用Insert(T value),不是吗?
  • 其实我在做什么,但我试图提高一堆适配器的性能
  • 好的,但我是说如果你担心性能,那么添加InsertValue(object value) 方法不会有太大变化。您仍然需要使用反射找到正确的 IAdapter 实例,这可能是缓慢的部分,因为它是一个反复执行的循环。唯一的小区别是方法调用本身可以在没有反射的情况下完成。要真正提高性能,您需要快速找到合适的 IAdapter,例如通过 Dictionary&lt;Type,IAdapter&gt; 将项目类型映射到 IAdapter 实例。
猜你喜欢
  • 1970-01-01
  • 2016-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多