1

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

假设我有这个界面:

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 中自动修改。

4

2 回答 2

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;
    }
}
于 2013-08-16T01:24:46.050 回答
0

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

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

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

public enum PersonChoice : byte
{
    Peter = 1,

    Charles = 2,

    Both = 3
}

这将用作全局配置值。我会将它作为一个属性存储在 Peter 中,这将是我将拦截的类。所以我定义了属性并添加了一些构造函数逻辑。

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

}

这是调用处理程序代码。使用一些反射,我得到了被调用的方法的名称及其参数。使用全局 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));
    }
}

最后,这是程序的输出:

人名:彼得姓氏:彼得麦考密克年龄:55

人名:查尔斯姓氏:查尔斯冈萨雷斯年龄:40

人名:博瑟盖特姓氏:博瑟盖特麦考密克博瑟盖特冈萨雷斯年龄:528

于 2013-08-20T15:50:15.673 回答