转换为派生较少的类型和处理泛型
本文关键字:类型 处理 泛型 派生 转换 | 更新日期: 2023-09-27 18:16:41
我试图写一些能够采取各种行为类,并将它们链接到一起的查询和命令链的东西。下面是我采用的一种方法,但是我遇到了将派生类型转换为基类型的问题。我必须处理泛型参数,我不确定这种方法是否可行,或者我是否需要定义一些隐式或显式转换操作符,或者其他一些东西。
这是基类型的集合。首先是最基本的界面:
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();
}
下一个抽象类将是由任何具体查询类实现的类型(与命令相反,命令没有result类型)。它设置了通过后继程序链接查询的能力。
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]'."}
我希望有一些方法来解决这个问题,而不必在每个具体的调用器中实现一些特别的东西。我最终可能会有几十个这样的具体类,每个类对泛型类型参数使用不同的类型。那么,有什么办法可以补救吗?
如果我明白你想要做什么,我认为你已经使你的代码有点复杂,可能是脆弱的。
告诉我这样做是否更适合你:
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>
{
}
将每个调用者强制转换为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
替换,因此强制转换失败。