12

我有一个在客户端应用程序和服务器应用程序中使用的类。在服务器应用程序中,我通过扩展方法向类添加了一些功能。效果很好。现在我想要更多:

我的类 (B) 继承自另一个类 (A)。

我想将一个虚函数附加到 A(比如说 Execute() ),然后在 B 中实现该函数。但仅限于服务器中。Execute() 方法将需要使用只有服务器知道的类型来执行只能在服务器上执行的操作。

有许多类型从 A 继承,就像 B 一样,我想为它们中的每一个实现 Execute()。

我希望我可以向 A 添加一个虚拟扩展方法,但这个想法似乎并没有实现。我正在寻找解决这个问题的最优雅的方法,不管有没有扩展方法。

4

6 回答 6

4

不,没有虚拟扩展方法之类的东西。您可以使用重载,但这不支持多态性。听起来您可能想查看依赖注入(等)之类的东西,以便在不同的环境中添加不同的代码(依赖项) - 并在常规虚拟方法中使用它:

class B {
     public B(ISomeUtility util) {
         // store util
     }
     public override void Execute() {
         if(util != null) util.Foo();
     }
}

然后使用 DI 框架在运行时提供特定ISomeUtility于服务器的实现B。您可以使用中央static注册表(IOC,但没有 DI)做同样的事情:

    override void Execute() {
        ISomeUtility util = Registry.Get<ISomeUtility>();
        if(util != null) util.Foo();
    }

(你需要在哪里写Registry等;加上在服务器上,注册ISomeUtility实现)

于 2009-04-17T07:59:07.517 回答
4

您可以使用新的动态类型功能来避免为方法构建类型注册表:

using System;
using System.Collections.Generic;
using System.Linq;
using visitor.Extension;

namespace visitor
{
    namespace Extension
    {
        static class Extension
        {
            public static void RunVisitor(this IThing thing, IThingOperation thingOperation)
            {
                thingOperation.Visit((dynamic)thing);
            }

            public static ITransformedThing GetTransformedThing(this IThing thing, int arg)
            {
                var x = new GetTransformedThing {Arg = arg};
                thing.RunVisitor(x);
                return x.Result;
            }
        }
    }

    interface IThingOperation
    {
        void Visit(IThing iThing);
        void Visit(AThing aThing);
        void Visit(BThing bThing);
        void Visit(CThing cThing);
        void Visit(DThing dThing);
    }

    interface ITransformedThing { }

    class ATransformedThing : ITransformedThing { public ATransformedThing(AThing aThing, int arg) { } }
    class BTransformedThing : ITransformedThing { public BTransformedThing(BThing bThing, int arg) { } }
    class CTransformedThing : ITransformedThing { public CTransformedThing(CThing cThing, int arg) { } }
    class DTransformedThing : ITransformedThing { public DTransformedThing(DThing dThing, int arg) { } }

    class GetTransformedThing : IThingOperation
    {
        public int Arg { get; set; }

        public ITransformedThing Result { get; private set; }

        public void Visit(IThing iThing) { Result = null; }
        public void Visit(AThing aThing) { Result = new ATransformedThing(aThing, Arg); }
        public void Visit(BThing bThing) { Result = new BTransformedThing(bThing, Arg); }
        public void Visit(CThing cThing) { Result = new CTransformedThing(cThing, Arg); }
        public void Visit(DThing dThing) { Result = new DTransformedThing(dThing, Arg); }
    }

    interface IThing {}
    class Thing : IThing {}
    class AThing : Thing {}
    class BThing : Thing {}
    class CThing : Thing {}
    class DThing : Thing {}
    class EThing : Thing { }

    class Program
    {
        static void Main(string[] args)
        {
            var things = new List<IThing> { new AThing(), new BThing(), new CThing(), new DThing(), new EThing() };
            var transformedThings = things.Select(thing => thing.GetTransformedThing(4)).Where(transformedThing => transformedThing != null).ToList();
            foreach (var transformedThing in transformedThings)
            {
                Console.WriteLine(transformedThing.GetType().ToString());
            }
        }
    }
}
于 2011-02-11T18:49:41.167 回答
2

我会建议类似以下内容。可以通过添加对检测没有调度映射的中间类层次结构类型的支持并根据运行时层次结构调用最近的调度方法来改进此代码。它还可以通过使用反射来检测 ExecuteInteral() 的过载并将它们自动添加到调度映射中来改进。

using System;
using System.Collections.Generic;

namespace LanguageTests2
{
    public class A { }

    public class B : A {}

    public class C : B {}

    public static class VirtualExtensionMethods
    {
        private static readonly IDictionary<Type,Action<A>> _dispatchMap 
            = new Dictionary<Type, Action<A>>();

        static VirtualExtensionMethods()
        {
            _dispatchMap[typeof(A)] = x => ExecuteInternal( (A)x );
            _dispatchMap[typeof(B)] = x => ExecuteInternal( (B)x );
            _dispatchMap[typeof(C)] = x => ExecuteInternal( (C)x );
        }

        public static void Execute( this A instance )
        {
            _dispatchMap[instance.GetType()]( instance );
        }

        private static void ExecuteInternal( A instance )
        {
            Console.WriteLine("\nCalled ToString() on: " + instance);
        }

        private static void ExecuteInternal(B instance)
        {
            Console.WriteLine( "\nCalled ToString() on: " + instance );
        }

        private static void ExecuteInternal(C instance)
        {
            Console.WriteLine("\nCalled ToString() on: " + instance);
        }
    }

    public class VirtualExtensionsTest
    {
        public static void Main()
        {
            var instanceA = new A();
            var instanceB = new B();
            var instanceC = new C();

            instanceA.Execute();
            instanceB.Execute();
            instanceC.Execute();
        }
    }
}
于 2009-04-17T15:41:03.570 回答
0

虚拟意味着以 OOP 方式继承,而扩展方法是“只是”静态方法,通过一点语法糖,编译器允许您假装调用其第一个参数类型的实例。所以不,虚拟扩展方法是不可能的。

查看 Marc Gravell 的答案,了解您的问题的可能解决方案。

于 2009-04-17T08:05:05.287 回答
0

您可以实现服务寄存器。示例(服务器端):

static IDictionary<Type, IService> serviceRegister;

public void ServerMethod(IBusinessType object)
{
  serviceRegister[obect.GetType()].Execute(object);
}

您需要的是服务器中的服务,它们实现服务器端功能,而不是扩展方法。我不会在扩展方法中投入太多逻辑。

于 2009-04-17T08:08:47.947 回答
0

让我检查一下:您有一个继承自 A 的类层次结构,大概是根据您的业务领域构建的。然后,您要根据类的执行位置添加行为。到目前为止,您已经使用了扩展方法,但现在您发现无法让它们随着您的类层次结构而变化。您在服务器上附加了哪些类型的行为?

如果是事务管理和安全之类的东西,通过依赖注入实现的策略 à la Marc 的建议应该很好用。您还可以考虑通过委托和 lambda 实现策略模式,以获得更有限的 DI 版本。但是,尚不清楚客户端代码当前如何使用您的类及其在服务器上的扩展方法。其他类对您如何添加服务器端功能的依赖程度如何?它们是当前期望找到扩展方法的仅服务器端类吗?

在任何情况下,听起来您都需要仔细的可测试性设计和测试策略,因为您要沿两个同时的维度(继承层次结构、执行环境)引入变化。我相信您正在使用单元测试?检查您选择的任何解决方案(例如,通过配置进行 DI)与测试和模拟的交互是否良好。

于 2009-04-17T08:39:40.633 回答