1

我正在尝试设计一种模式来协调多个操作。每个操作都会接受一个参数并传递一个结果。该结果可能会或可能不会被以下操作使用。这是设计的简化版本,但是如果您将其复制/粘贴到控制台项目上,它将“工作”(有一个我无法修复的编译错误)。

错误

类型“ConsoleApplication1.InternalDebit”不能用作泛型类型或方法“ConsoleApplication1.Orchestrator.Add(T1)”中的类型参数“T1”。没有从“ConsoleApplication1.InternalDebit”到“ConsoleApplication1.Operation”的隐式引用转换。c:\projects\BCP\BaseMvc\ConsoleApplication1\ConsoleApplication1\Program.cs 17 13 ConsoleApplication1

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var internalDebit = new InternalDebit<InternalDebitParameter, InterbankCreditParameter>(new InternalDebitParameter() { Id = 1 });

            var orchestrator = new Orchestrator();

            // error here!
            orchestrator.Add(internalDebit);
        }
    }

    public interface IParameter
    {
    }

    public interface IResult
    {
    }

    public interface IReversible
    {
        void Reverse();
    }


    public interface IOperation<T, R>
        where T : class, IParameter
        where R : class, IResult
    {
        Type ParameterType { get; }

        Type ResultType { get; }

        T Parameter { get; set; }

        R Execute(T parameter);
    }

    public abstract class Operation<T, R> : IOperation<T, R>
        where T : class, IParameter
        where R : class, IResult
    {
        public virtual R Execute(T parameter)
        {
            this.Parameter = parameter;
            return default(R);
        }

        public Type ParameterType
        {
            get { return typeof(T); }
        }

        public Type ResultType
        {
            get { return typeof(R); }
        }

        public T Parameter { get; set; }

        public Operation(T parameter)
        {
            this.Parameter = parameter;
        }
    }

    public class InternalDebitParameter : IParameter
    {
        public int Id { get; set; }
    }

    public class InterbankCreditParameter : IParameter, IResult
    {
        public int Id { get; set; }
    }



    public class InternalDebit<T, R> : Operation<T, R>
        where T : class, IParameter
        where R : class, IResult
    {
        public InternalDebit(T parameter)
            : base(parameter)
        {
        }

        public override R Execute(T parameter)
        {
            return new InterbankCreditParameter() { Id = 2 } as R;
        }
    }

    public class Orchestrator
    {
        public List<Operation<IParameter, IResult>> Operations { get; private set; }

        public List<IParameter> Parameters { get; private set; }

        public void Add<T1>(T1 t) where T1 : Operation<IParameter, IResult>
        {
            this.Operations.Add(t);
        }

        public void SetUpParameters(params IParameter[] parameters)
        {
            this.Parameters = new List<IParameter>();

            parameters.ToList().ForEach(s => this.Parameters.Add(s));
        }

        public void Play()
        {
            IParameter generalResult = null;

            foreach (var instrument in this.Operations)
            {
                var parameter = this.Parameters.FirstOrDefault(s => s.GetType() == instrument.ParameterType);

                if (parameter == null)
                {
                    IResult actualResult = null;

                    if (generalResult != null)
                    {
                        try
                        {
                            actualResult = instrument.Execute(generalResult);
                        }
                        catch (Exception ex)
                        {
                            if (instrument is IReversible)
                                ((IReversible)instrument).Reverse();
                            else
                                throw;
                            break;
                        }
                        finally
                        {
                            if (actualResult is IParameter)
                                generalResult = (IParameter)actualResult;
                        }
                    }
                    else
                    {
                        throw new Exception("Orchetrator missconfiguration.");
                    }
                }
            }
        }
    }
}
4

3 回答 3

1

您将泛型带入 C++ 模板功能的范围太广了。在给出错误的行上,您正在隐式创建函数:

 public void Add(InternalDebit<InternalDebitParameter, InterbankCreditParameter>);

正如声明的那样,这个类继承自:

 Operation<InternalDebitParameter, InterbankCreditParameter>

然而,通用要求指出 T1 应该是 type Operation<IParameter, IResult>,但事实并非如此,即使两个参数都从正确的类型继承,因为不允许多态性。

您在这里尝试实现的目标对于泛型(实际上是 C++ 中的模板)本质上是不可能的,因为您指定的方式太多,并且指定了永远无法满足的继承要求。您需要记住,泛型在某种程度上只是用少量代码编写许多类的一种奢侈的简写方式,它们不会突然引入递归多态性。

长话短说,重写代码以使用继承和基类,而不是依赖泛型。我怀疑您的整个模式在没有单一泛型的情况下是可能的,并且类型安全。

于 2013-04-23T20:54:49.073 回答
1

如果你玩一点协方差/逆变,你可能会做一些类似于你所追求的事情。或者无论如何,编译器会更准确地告诉您您尝试执行的操作不是类型安全的。

第一步:您收到的错误表明There is no implicit reference conversion from 'InternalDebit<InternalDebitParameter,InterbankCreditParameter>' to 'Operation<IParameter,IResult>'.

因此,自从InternalDebitimplements以来IOperation,您可以做的第一件事就是使IOperation协变,尝试将其定义为:

public interface IOperation<out T, out R>

这意味着 type 的变量IOperation<IParameter,IResult>会很乐意接受 type 的值Operation<InternalDebitParameter,InterbankCreditParameter>,这更接近您想要的值。然后,您Add的方法签名将被限制IOperationOperation

public void Add<T1>(T1 t) where T1 : IOperation<IParameter, IResult>

编译器告诉我们出了点问题:

Invalid variance: The type parameter 'T' must be invariantly valid on 'IOperation<T,R>.Parameter'. 'T' is covariant.

Invalid variance: The type parameter 'T' must be contravariantly valid on 'IOperation<T,R>.Execute(T)'. 'T' is covariant.

这是为什么这段代码不健全的第一个迹象。协变参数只能在函数的“出路”(即作为返回类型)中使用,而不能作为“入”参数。

第二步使 IOperation 协变。这可能会很痛苦,并且会更改您的代码,因为这意味着将 Execute 更改为不接受 T 类型的参数。

public interface IOperation<out T, out R>
    where T : class, IParameter
    where R : class, IResult
{
    Type ParameterType { get; }

    Type ResultType { get; }

    T Parameter { get; /*set;*/ } //can't allow the interface to set T 

    // R Execute(T parameter); // can't have an Execute with T as a parameter
    R Execute(); // you can however inject T in the constructor of the
                 // inherited class and call Execute without parameters    
}

第三步你现在得到一个新的错误:

The best overloaded method match for 'System.Collections.Generic.List<Operation<IParameter,IResult>>.Add(Operation<IParameter,IResult>)' has some invalid arguments

这又是一个协方差问题。列表不是协变的,您不能将 t 添加到列表中。我真的不知道该建议什么,因为我不想完全改变你的代码的意图(特别是因为我不能说我完全理解它......)你可能会在这个答案中找到一些有用的东西,因为实例:

协方差和 IList

于 2013-04-24T11:10:08.123 回答
1

好的,为了这篇文章的完整性,我将向您展示我最终是如何使这个工作的。它可以更好,我仍然愿意接受建议。不幸的是,我不得不从这项任务中继续前进,它已经被推迟了。

我将发布并编辑此答案,以便在代码审查网站上跟进。

在控制台应用程序中复制/粘贴,这是一个功能齐全的代码示例。

class Program
    {
        static void Main(string[] args)
        {
            var transferenceInfo = new InterbankTranferenceInfo();

            var orchestrator = new Orchestrator(new InternalDebitOperation(transferenceInfo),
                                                new InterbankCreditOperation(),
                                                new CommissionOperation());

            orchestrator.Run();
        }
    }

    public class InterbankTranferenceInfo : IParameter
    {
        public bool InternalDebitDone { get; set; }

        public bool InterbankCreditDone { get; set; }

        public bool CommissionDone { get; set; }
    }

    public class InternalDebitOperation : Operation<InterbankTranferenceInfo>, IOperation<InterbankTranferenceInfo>
    {
        public InternalDebitOperation(InterbankTranferenceInfo parameter)
            : base(parameter)
        {
        }

        public override InterbankTranferenceInfo Execute()
        {
            return new InterbankTranferenceInfo() { InternalDebitDone = true };
        }
    }

    public class InterbankCreditOperation : Operation<InterbankTranferenceInfo>, IOperation<InterbankTranferenceInfo>
    {
        public override InterbankTranferenceInfo Execute()
        {
            Parameter.InterbankCreditDone = true;

            return Parameter;
        }
    }

    public class CommissionOperation : Operation<InterbankTranferenceInfo>, IReversible, IOperation<InterbankTranferenceInfo>
    {
        public override InterbankTranferenceInfo Execute()
        {
            Parameter.CommissionDone = true;

            // Uncomment this code to test Reverse operation.
            // throw new Exception("Test exception, it should trigger Reverse() method.");

            return Parameter;
        }

        public void Reverse()
        {
            Parameter.CommissionDone = false;
        }
    }

    public enum OperationStatus
    {
        Done,
        Pending,
        Reversed
    }

    public interface IParameter
    {
    }

    public interface IReversible
    {
        void Reverse();
    }

    public interface IOperation<out T> : IInternalOperation<T>  where T : IParameter
    {
    }

    public interface IInternalOperation<out T> : IExecutableOperation<T>
    {
        bool GetParameterFromParentOperation { get; }

        OperationStatus Status { get; set; }

        IParameter Execute(IParameter parameter);      
    }

    public interface IExecutableOperation<out T>
    {
        T Execute();
    }

    //[System.Diagnostics.DebuggerStepThroughAttribute()]
    public abstract class Operation<T> : IInternalOperation<T> where T : IParameter
    {
        public T Parameter { get; private set; }

        public bool GetParameterFromParentOperation { get { return this.Parameter == null; } }

        public OperationStatus Status { get; set; }

        public Operation()
        {
            Status = OperationStatus.Pending;
        }

        public Operation(IParameter parameter)
        {
            Status = OperationStatus.Pending;
            this.Parameter = (T)parameter;
        }

        public abstract T Execute();

        public virtual IParameter Execute(IParameter parameter)
        {
            this.Parameter = (T)parameter;
            return this.Execute();
        }
    }

    public class Orchestrator
    {
        public List<IOperation<IParameter>> Operations { get; private set; }

        public Orchestrator(params IOperation<IParameter>[] operations) 
        {
            this.Operations = new List<IOperation<IParameter>>();

            foreach (var item in operations)
            {
                this.Operations.Add((IOperation<IParameter>)item);
            }
        }

        public IParameter Run()
        {
            IParameter previousOperationResult = null;

            foreach (var operation in this.Operations)
            {
                try
                {
                    if (operation.GetParameterFromParentOperation)
                        previousOperationResult = operation.Execute(previousOperationResult);
                    else
                        previousOperationResult = operation.Execute();

                    operation.Status = OperationStatus.Done;
                }
                catch (Exception)
                {
                    foreach (var o in this.Operations)
                    {
                        if (o is IReversible)
                        {
                            ((IReversible)o).Reverse();
                            o.Status = OperationStatus.Reversed;
                        }
                        else
                            throw;
                    }
                    break;
                }
            }

            return previousOperationResult;
        }
    }

编辑

代码审查帖子

于 2013-04-24T15:56:09.490 回答