转换为派生较少的类型和处理泛型

本文关键字:类型 处理 泛型 派生 转换 | 更新日期: 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替换,因此强制转换失败。