具有多态性的 C# Func<Interface>
C# Func<Interface> with polymorphism
我正在尝试为我的数据结构实现访问者模式,它基于 class 层次结构。
在 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.
- 访客模式是一种手动实现双重调度的方式。
- 使用
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);
您需要两个接口。一种用于访问者,一种用于可访问者 classes
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);
}
算法 class 现在可以像这样实现 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.
}
但是,return 来自 Accept
或 Visit
方法的结果是不寻常的,因为访问者的任务是做一些有用的事情。它不是迭代器或接受对象的任务。这使您能够创建执行完全不同的事情的访问者。
也许 Strategy Pattern would better suit your needs than the Visitor Pattern.
我有点不清楚您要归档的内容,但是对于快速运行时类型绑定,您可以使用以下代码:
您可以按如下方式使用 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);
}
}
现在在您的代码中,您可以像这样使用 class:
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...
}
在运行时使用 dynamic
或 System.Reflection
的方法 Binder
会减慢您的程序(尽管使用 object
作为参数需要 装箱、拆箱 和在方法开头进行铸造).
我正在尝试为我的数据结构实现访问者模式,它基于 class 层次结构。 在 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.
- 访客模式是一种手动实现双重调度的方式。
- 使用
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);
您需要两个接口。一种用于访问者,一种用于可访问者 classes
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);
}
算法 class 现在可以像这样实现 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.
}
但是,return 来自 Accept
或 Visit
方法的结果是不寻常的,因为访问者的任务是做一些有用的事情。它不是迭代器或接受对象的任务。这使您能够创建执行完全不同的事情的访问者。
也许 Strategy Pattern would better suit your needs than the Visitor Pattern.
我有点不清楚您要归档的内容,但是对于快速运行时类型绑定,您可以使用以下代码:
您可以按如下方式使用 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);
}
}
现在在您的代码中,您可以像这样使用 class:
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...
}
在运行时使用 dynamic
或 System.Reflection
的方法 Binder
会减慢您的程序(尽管使用 object
作为参数需要 装箱、拆箱 和在方法开头进行铸造).