I'm trying to implement the visitor pattern for my data structure, which is based on a class hierarchy. In C# you can't switch on types (yet). I was thinking about doing something like this as a replacement:
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) {}
}
And then calling it with late bound dispatch:
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.
}
However, this won't work, since MyAlgorithm
can't inherit from the delegate Func<...>
.
The only solution I saw is to define a lot of interfaces of my own, like the following. Is there a better way?
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
enables multiple dispatch.If your goal is simply to choose a function based on the runtime type of the argument, then picking one of these two options would suffice - there's no point in combining them.
Here's a solution that uses dynamic
instead of a visitor:
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;
}
}
You can use it like this:
var algorithm = new MyAlgorithm();
var data = new ConcreteSubclass();
algorithm.Apply(data);
And, alternatively, you can use a delegate like this:
Func<IBase, X> func = algorithm.Apply;
func(data);