虚拟扩展方法?

时间:2009-04-17 07:48:23

标签: c# extension-methods virtual

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

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

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

有许多类型从B继承A,我想为每个类型实现Execute()。

我希望我能为A添加一个虚拟扩展方法,但这个想法似乎没有。无论有没有扩展方法,我都在寻找解决这个问题的最优雅的方法。

6 个答案:

答案 0 :(得分:4)

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

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实现)

答案 1 :(得分:3)

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

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());
            }
        }
    }
}

答案 2 :(得分: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();
        }
    }
}

答案 3 :(得分:0)

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

查看Marc Gravell的答案,找出解决问题的方法。

答案 4 :(得分:0)

您可以实施服务注册。示例(服务器端):

static IDictionary<Type, IService> serviceRegister;

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

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

答案 5 :(得分:0)

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

如果它是交易管理和安全性之类的东西,通过依赖注入Marc的建议实施的政策应该运作良好。您还可以考虑通过委托和lambdas实现Strategy pattern,以获得更有限的DI版本。但是,目前尚不清楚客户端代码当前如何在服务器上使用您的类及其扩展方法。其他类如何依赖于如何添加服务器端功能?它们是目前期望找到扩展方法的服务器端类吗?

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