组合和拦截由同一接口定义的2个类的方法

时间:2013-08-15 19:22:33

标签: c# interface intercept

我有一系列实现接口的类。我想创建一个第三类(也实现接口),它将两个类的方法组合成一个,再加上一些管理代码。 换句话说。

假设我有这个界面:

public interface ITestClass
{
    string NamePlusLastName(string name);

    int AgePlus20(int age);

    DateTime YearYouWillDie(int age);
}

我有两个实现它的类:

public class TestClassPeter : ITestClass
{
    public string NamePlusLastName(string name)
    {
        return string.Concat(name, " ", "Mc.Cormick");
    }

    public int AgePlus20(int age)
    {
        return age + 40;
    }

    public DateTime YearYouWillDie(int age)
    {
        return DateTime.Now;
    }
}

public class TestClassCharles : ITestClass
{
    public string NamePlusLastName(string name)
    {
        return string.Concat(name, " ", "Gonzales");
    }

    public int AgePlus20(int age)
    {
        return age + 20;
    }

    public DateTime YearYouWillDie(int age)
    {
        return DateTime.Now ;
    }
}

我想创建一个返回实现ITestClass的对象的类,其方法是两个类的组合。如:

public class TestBoth 
{
    ITestClass peter;
    ITestClass charles;
    ITestClass combinedClass;

    public TestBoth()
    {
        // instantiate classes to be combined.
        peter = new TestClassPeter();
        charles = new TestClassCharles();

        // BEG
        // Add here code that will automatically combine methods of both classes into one.
        // ......
        // END

    }

    // expose as property
    public ITestClass CombinedClass
    {
        get
        {
            return combinedClass;
        }
    }
}

所以最后我可以像这样调用组合类:

TestBoth classBuilder = new TestBoth();
ITestClass combinedClass = classBuilder.combinedClass;

// call a method
string fullName = combinedClass.NamePlusLastName("William");

幕后发生的事情真的是:

string firstValue = peter.NamePlusLastName("William");
// some code here
string secondValue = charles.NamePlusLastName("William");
// some more code here
return finalValue;

我希望所有方法都能自动执行此操作。这样,如果我更改了接口定义,以及Peter和Charles的实现,它将在classBuilder中自动修改。

2 个答案:

答案 0 :(得分:0)

只需定义一个调用两个实现的第三个类,以某种方式组合它们的值并返回该结果,实现接口本身。我认为不需要 TestBoth ,除非你想动态构建代码,这是一种完全不同的方式,而且更加困难。我会做这样的事情:

public class TestClassPeterAndCharles : ITestClass
{
    private readonly ITestClass peter;
    private readonly ITestClass charles;

    public TestClassPeterAndCharles()
    {
        // Create helper objects from base implementation
        // Alternatively, take them as constructor parameters
        this.peter = new TestClassPeter();
        this.charles = new TestClassCharles();
    }

    public string NamePlusLastName(string name)
    {
        // Get the result from each class and operate on them
        string namePeter = this.peter.NamePlusLastName(name);
        string nameCharles = this.charles.NamePlusLastName(name);
        return namePeter + nameCharles;
    }

    public int AgePlus20(int age)
    {
        int agePeter = this.peter.AgePlus20(age);
        int ageCharles = this.charles.AgePlus20(age);
        return agePeter + ageCharles;
    }

    public DateTime YearYouWillDie(int age)
    {
        DateTime yearPeter = this.peter.YearYouWillDie(age);
        DateTime yearCharles = this.charles.YearYouWillDie(age);
        return yearPeter + yearCharles;
    }
}

答案 1 :(得分:0)

好的,经过一些研究和测试后,我认为我找到了一个可行的解决方案。它需要使用Unity Intercept和一些反射。

我将使用Unity创建其方法将被截获的类。我将该类默认为Peter,然后让CallHandler根据配置值确定从哪个类触发哪些方法。

首先,我定义了一个枚举,它将指定我希望使用的对象组合(Peter,Charles或Both):

public enum PersonChoice : byte
{
    Peter = 1,

    Charles = 2,

    Both = 3
}

这将作为全局配置值。我将把它存放在彼得的财产中,这将是我将要拦截的类。所以我定义了属性并添加了一些构造函数逻辑。

public class TestClassPeter : ITestClass
{
    private PersonChoice personChoice;

    // Default Constructor
    public TestClassPeter()
    {
    }

    // Constructor where global personChoice is stored
    public TestClassPeter(PersonChoice personChoice)
    {
        this.personChoice = personChoice;
    }

    public string NamePlusLastName(string name)
    {
        return string.Concat(name, " ", "Mc.Cormick");
    }

    public int AgePlus20(int age)
    {
        return age + 40;
    }

    public DateTime YearYouWillDie(int age)
    {
        return DateTime.Now;
    }

    public PersonChoice PersonChoice
    {
        get{return personChoice;}
    }
}

我还需要向Charles添加一个默认构造函数:

public class TestClassCharles : ITestClass
{
    public TestClassCharles()
    {
    }
 ...
}

我需要告诉界面定义Peter和Charles哪些方法会被截获。为此,我创建了一个Handler属性

public class ChoosePersonAttribute : HandlerAttribute
{
    public override ICallHandler CreateHandler(IUnityContainer container)
    {
        return new PersonChoiceHandler();
    }

}

然后将该属性应用于我的界面

public interface ITestClass
{

    [ChoosePerson]               
    string NamePlusLastName(string name);

    [ChoosePerson]   
    int AgePlus20(int age);

    DateTime YearYouWillDie(int age);
}

接下来,我定义了一个类,它将为我提供一个准备被拦截的对象实例。 (注意容器有PersonChoice作为构造函数的参数)

public static class Arbitrator
{
    public static ITestClass getInstance(PersonChoice personChoice)
    {
        // Initialize container
        IUnityContainer unityContainer = new UnityContainer();
        unityContainer.AddNewExtension<Interception>();

        // Associate Interface with object to be intercepted
        unityContainer.RegisterType<ITestClass, TestClassPeter>(new InjectionConstructor(personChoice));
        unityContainer.Configure<Interception>()
            .SetInterceptorFor<ITestClass>(new InterfaceInterceptor());

        // return instance
        return unityContainer.Resolve<ITestClass>();

    }

}

现在,我创建了一种基于personChoice获取实例的外观

public class TestFacade
{
    private ITestClass testClass;

    public TestFacade(PersonChoice personChoice)
    {

        // Get Instance of primary object
        ITestClass combinedClass = Arbitrator.getInstance(personChoice);

        testClass = combinedClass;

    }

    // expose property
    public ITestClass ITester
    {
        get
        {
            return testClass;
        }
    }

}

这是调用Handler代码。使用一些反射,我得到被调用方法的名称及其参数。使用全局personChoice参数我可以告诉这个处理程序实例化哪些类,然后决定(仲裁)要调用的方法。对于此示例,对于“Both”选项,我只是决定添加返回值。

public class PersonChoiceHandler : ICallHandler
{
    private WriteOption writeOption;

    private PersonChoice personChoice;

    public PersonChoiceHandler(WriteOption writeOption)
    {
        this.writeOption = writeOption;
    }

    public PersonChoiceHandler()
    {
        this.writeOption = WriteOption.Both;
    }

    public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
    {
        // Get personChoice value from object property.
        personChoice = (PersonChoice)Enum.Parse(typeof(PersonChoice),  input.Target.GetType().GetProperty("PersonChoice").GetValue(input.Target, null).ToString());

        // Get Method Name and parameters
        string methodName = input.MethodBase.Name;
        object[] methodArgs = new object[input.Inputs.Count];
        for (int i = 0; i < input.Inputs.Count; i++)
        {
            methodArgs[i] = input.Inputs[i];
        }

        Type firstPersonType = null;
        Type secondPersonType = null;
        object firstPersonObject;
        object secondPersonObject;

        // based on personChoice value, instantiate appropriate class and execute the appropriate method .
        switch (personChoice)
        {
            case PersonChoice.Peter:
                firstPersonType = typeof(TestClassPeter);
                break;
            case PersonChoice.Charles:
                firstPersonType = typeof(TestClassCharles);
                break;
            case PersonChoice.Both:
                firstPersonType = typeof(TestClassPeter);
                secondPersonType = typeof(TestClassCharles);
                break;
            default:
                break;
        }


        // object is instantiated with default constructor. No need to specify PersonChoice property.
        firstPersonObject = Activator.CreateInstance(firstPersonType);
        if (personChoice == PersonChoice.Both)
        {
            secondPersonObject = Activator.CreateInstance(secondPersonType);
        }
        else
        {
            secondPersonObject = null; ;
        }

        // decide method invocation based on PersonChoice
        object firstReturnValue;
        object secondReturnValue;
        switch (personChoice)
        {
            // Call Peter's or Charles' methods
            case PersonChoice.Peter : case PersonChoice.Charles:
                firstReturnValue = firstPersonType.InvokeMember(methodName, BindingFlags.InvokeMethod, null, firstPersonObject, methodArgs);
                break;

            // Call Method on Both Peter and Charles and combine results
            case PersonChoice.Both :
                firstReturnValue = firstPersonType.InvokeMember(methodName, BindingFlags.InvokeMethod, null, firstPersonObject, methodArgs);
                secondReturnValue = secondPersonType.InvokeMember(methodName, BindingFlags.InvokeMethod, null, secondPersonObject, methodArgs);

                // build return value. Done here checking method name as an example.
                if (methodName == "NamePlusLastName")
                {
                    string returnValue = (string)firstReturnValue;
                    firstReturnValue = returnValue + (string)secondReturnValue;
                }
                else
                {
                    int returnValue = (int)firstReturnValue;
                    firstReturnValue = returnValue + (int)secondReturnValue;
                }

                break;

            default:
                firstReturnValue = firstPersonType.InvokeMember(methodName, BindingFlags.InvokeMethod, null, firstPersonObject, methodArgs);
                break;
        }

        // Override initial method execution
        IMethodReturn methodReturn = new VirtualMethodReturn(input, null);

        // this down here would have called the original method.
        //var methodReturn = getNext().Invoke(input, getNext);

        // Set the return value
        methodReturn.ReturnValue = firstReturnValue;

        return methodReturn;

    }

    public int Order { get; set; }
}

以下是执行调用的示例程序:

class TestClass
{
    static void Main()
    {
        // instantiate my facades
        TestFacade peterFacade = new TestFacade(PersonChoice.Peter);
        TestFacade charlesFacade = new TestFacade(PersonChoice.Charles);
        TestFacade bothFacade = new TestFacade(PersonChoice.Both);

        //      run some methods:
        // Peter
        string name = "Peter";
        int age = 15;
        writeProperties(peterFacade, name, age);

        // Charles
        name = "Charles";
        age = 20;
        writeProperties(charlesFacade, name, age);

        // Both
        name = "Bothergate";
        age = 234;
        writeProperties(bothFacade, name, age);

        // wait for user input.
        Console.ReadLine();
    }

    static void writeProperties(TestFacade facade, string name, int age)
    {
        Console.WriteLine("Person name: {0} Last Name: {1} Age : {2} ", name, facade.ITester.NamePlusLastName(name), facade.ITester.AgePlus20(age));
    }
}

最后,这是程序的输出:

姓名:Peter姓:Peter Mc.Cormick年龄:55

姓名:Charles姓:Charles Gonzales年龄:40

姓名:Bothergate姓:Bothergate Mc.CormickBothergate Gonzales年龄:528