2

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

这是我可以完成的事情还是仍然不可能?我已经知道使用抽象类是解决这个问题的一种方法,但我想让开发人员拥有一个实现许多 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 });
                }
            }

        }
    }
4

2 回答 2

1

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

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

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

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

于 2019-08-22T10:14:00.510 回答
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 提出的解决方案)

于 2019-08-22T10:32:44.457 回答