我正在尝试编写能够采用各种行为类并将它们链接在一起成为一系列查询和命令的东西。我采取的一种方法如下,但我遇到了将派生类型转换为基类型的问题。我不得不处理泛型参数,我不确定这种方法是不可能的,或者我是否需要定义一些隐式或显式转换运算符,或者完全不需要其他东西。
这是一组基本类型。首先是最基本的界面:
public interface IInvoker
{
void Invoke();
}
这个抽象类增加了拥有“主题”的能力,这是通过其中一个成员执行命令或查询的东西。它推迟实现Invoke方法:
public abstract class AbstractInvoker<TSubject> : IInvoker
{
protected TSubject Subject;
public void SetSubject(TSubject subject)
{
Subject = subject;
}
public abstract void Invoke();
}
下一个抽象类将是由任何具体查询类实现的类型(与命令相反,它不具有TResult类型)。它设置了通过Successor链接查询的能力。
public abstract class AbstractQueryInvoker<TSubject, TResult> : AbstractInvoker<TSubject>
{
protected AbstractInvoker<TResult> Successor;
public void SetSuccessor(AbstractInvoker<TResult> successor)
{
Successor = successor;
}
public override void Invoke()
{
var result = DoQuery();
Successor.SetSubject(result);
Successor.Invoke();
}
protected abstract TResult DoQuery();
}
实际查询逻辑通过DoQuery()
方法在具体类中实现。
我这样设置,所以我可以将查询链接在一起:
private List<IInvoker> _invokers;
// Build the list of various concrete classes
for (int i = 0; i < _invokers.Count - 1; i++)
{
((AbstractQueryInvoker<dynamic, dynamic>)_invokers[i]).SetSuccessor(
(AbstractInvoker<dynamic>)
_invokers[i + 1]);
}
我的目标是让每个调用者(除了最后一个)都将其后继者链接在这里,所以我需要做的就是在第一个元素上调用Invoke()
。但是,for循环中的第一个强制转换不起作用(第二个也许不是,我猜)。错误消息如下所示:
{"Unable to cast object of type 'ConcreteQueryInvoker' to type 'AbstractQueryInvoker`2[System.Object,System.Object]'."}
我希望有一些方法来解决这个问题而不必在每个具体的Invoker中实现特定的东西。我最终可能会有几十个具体类,每个类使用不同类型的泛型类型参数。那么,有没有办法解决这个问题?
答案 0 :(得分:1)
如果我明白你要做什么,我认为你的代码有点复杂,可能很脆弱。
告诉我这样的事情是否适合你:
IInvoker<string> invoker =
5
.CreateBehaviour()
.AddBehaviour(n => n * 2)
.AddBehaviour(n => n + 1)
.AddBehaviour(n => n.ToString())
.AddBehaviour(n => n + "!");
Console.WriteLine(invoker.Invoke());
在运行每个步骤后输出"11!"
。
执行此操作的代码是:
public static class BehaviorsEx
{
private class Subject<TSubject> : IInvoker<TSubject, TSubject>
{
private TSubject _subject;
public Subject(TSubject subject)
{
_subject = subject;
}
public TSubject Invoke() { return _subject; }
}
private class Invoker<TSubject, TResult> : IInvoker<TSubject, TResult>
{
private IInvoker<TSubject> _inner;
private Func<TSubject, TResult> _behaviour;
public Invoker(IInvoker<TSubject> inner, Func<TSubject, TResult> behaviour)
{
_inner = inner;
_behaviour = behaviour;
}
public TResult Invoke()
{
var x = _inner.Invoke();
return _behaviour(x);
}
}
public static IInvoker<TSubject> CreateBehaviour<TSubject>(this TSubject @this)
{
return new Subject<TSubject>(@this);
}
public static IInvoker<TResult> AddBehaviour<TSubject, TResult>(this IInvoker<TSubject> @this, Func<TSubject, TResult> behaviour)
{
return new Invoker<TSubject, TResult>(@this, behaviour);
}
}
public interface IInvoker<TResult>
{
TResult Invoke();
}
public interface IInvoker<TSubject, TResult> : IInvoker<TResult>
{
}
答案 1 :(得分:0)
将每个调用者强制转换为dynamic
:
// Build the list of various concrete classes
for (int i = 0; i < _invokers.Count - 1; i++)
{
((dynamic)_invokers[i]).SetSuccessor((dynamic)_invokers[i + 1]);
}
为什么原始代码不起作用
虽然AbstractQueryInvoker<dynamic, dynamic>
使用dynamic
关键字,但实际上它不是动态类型dynamic
本身就是动态的。在运行时,dynamic
类型参数被object
替换,因此强制转换失败。