C# Func<Interface> with polymorphism

本文关键字:with polymorphism gt Interface lt Func | 更新日期: 2023-09-27 18:15:12

我试图为我的数据结构实现访问者模式,这是基于类层次结构的。在c#中,你还不能切换类型。我正在考虑做这样的事情作为替代:

public MyAlgorithm : Func<IBase, X> {
    // default:
    public X apply(IBase data) {}
    // case param1 is ConcreteSubclass
    public X apply(ConcreteSubclass data) {}
    // case param1 is ConcreteOtherClass
    public X apply(ConcreteOtherClass data) {}
}

然后用后期绑定调度调用它:

public ICanApplyAlgorithmOn {
    public void apply(Func<IBase> alg);
    public TResult apply<TResult>(Func<IBase,TResult> alg);
    public TResult apply<TParam1,TResult>(Func<IBase, TParam1, TResult> alg);
    // etc.
}
public abstract Base : ICanApplyAlgorithmOn {
    // etc.
    public TResult apply(Func<IBase, X> alg) {
        // Hopefully I am correct in deducing that this will call the various apply(...) implementations instead of always method(IBase)
        dynamic me = this;
        return alg(me);
    }
    // etc.
}

但是,这不起作用,因为MyAlgorithm不能继承委托Func<...>

我看到的唯一解决方案是定义我自己的许多接口,如下所示。有没有更好的办法?

public interface IAlgorithm { public void apply(IBase data); }
public interface IAlgorithm<TReturn> { public TReturn apply(IBase data); }
public interface IAlgorithm<TP1, TReturn> { public TReturn apply(IBase data, TP1 param1); }
// etc.

C# Func<Interface> with polymorphism

  • 访客模式是一种手动实现双重调度的方式。
  • 使用dynamic可以实现多路调度。

如果您的目标只是根据参数的运行时类型选择一个函数,那么从这两个选项中选择一个就足够了——没有必要将它们组合在一起。

下面是一个使用dynamic而不是访问者的解决方案:

class MyAlgorithm
{
    public X Apply(IBase data)
    {
        try
        {
            return ApplyImpl((dynamic) data);
        }
        catch (RuntimeBinderException ex)
        {
            throw new ArgumentException(
                string.Format("{0} is not implemented for type {1}.", typeof (MyAlgorithm).Name, data.GetType().Name),
                ex);
        }
    }
    private X ApplyImpl(ConcreteSubclass sub)
    {
        // Your implementation here.
        return null;
    }
    private X ApplyImpl(ConcreteOtherClass sub)
    {
        // Your implementation here.
        return null;
    }
}

你可以这样使用:

var algorithm = new MyAlgorithm();
var data = new ConcreteSubclass();
algorithm.Apply(data);

或者,你可以使用这样的委托:

Func<IBase, X> func = algorithm.Apply;
func(data);

您需要两个接口。一个用于访问者,一个用于可访问类

public interface IAlgorithmVisitor<X>
{
    public X Visit(IBase data);
    public X Visit(ConcreteSubclass data);
    public X Visit(ConcreteOtherClass data);
}
public interface IAlgorithmVisitable<X>
{
    X Accept(IAlgorithmVisitor<X> visitor);
}

算法类现在可以像这样实现Accept方法:

public X Accept(IAlgorithmVisitor<X> visitor)
{
    return visitor.Visit(this);
}

注意,方法重载机制会根据当前类型自动调用Visit的正确重载。正确的方法重载是在编译时解决的!(不需要动态绑定)


现在,你可以像这样迭代一组算法

IAlgorithmVisitor<int> visitor = new ConcreteAlgorithmVisitor<int>();
foreach (var algorithm in intAlgorithms) {
    int result = algorithm.Accept(visitor);
    //TODO: do something with result.
}

然而,从AcceptVisit方法返回结果是不寻常的,因为访问者的任务是做一些有用的事情。它不是迭代器或接受对象的任务。这使您能够创建执行完全不同功能的访问者。

也许策略模式比访问者模式更适合您的需要。

我有点不清楚你想要存档什么,但对于快速运行时类型绑定,你可以使用以下代码:

可以这样使用Dictionary<Type, Func<object, TReturn>>:

public class AlgorithmClass<TReturn>
{
    private Dictionary<Type, Func<object, TReturn>> mMethods;
    public AlgorithmClass<TReturn>(Dictionary<Type, Func<object, TReturn>> methods)
    {
        mMethods = methods
    }
    public TReturn Invoke(object argument)
    {
        Type type = argument.GetType();
        //This line supports inheritance and co/contra-variance.
        //If you want to archive full performance and not support those features you can just use mMethods.TryGetValue(type, out Func<object, TReturn>);
        var kvps = mMethods.Where(x => x.Key.IsAssignableFrom(type));
        if(!kvp.Any())
        {
            throw new MissingMethodException("There is no method which can take " + type.Name + " as an argument");
        }
        if(kvp.Count() > 1)
        {
            throw new ArgumentException("There is more than one method which can take " + type.Name + " as an argument");
        }
        return kvp.First().Value(argument);
    }
}

现在在你的代码中你可以这样使用类:

AlgorithmClass<ReturnType> algorithm = new AlgorithmClass(new Dictionary<Type, Func<object, ReturnType>>
    {
        {typeof(int), MethodForIntType},
        {typeof(string), MethodForStringType},
        {typeof(MyClass), MethodForMyClassType}
    });
ReturnType MethodForIntType(object anInt)
{
    code...
}
ReturnType MethodForStringType(object aString)
{
    code...
}
ReturnType MethodForMyClassType(object aMyClass)
{
    code...
}

在运行时使用dynamicSystem.Reflection的方法Binder会减慢程序的速度(尽管使用object作为参数需要装箱拆箱在方法开始时进行强制转换)。