C# 合并并截取由同一接口定义的2个类的方法

C# 合并并截取由同一接口定义的2个类的方法,c#,interface,intercept,C#,Interface,Intercept,我有一系列实现接口的类。我想创建一个第三个类(它也实现了接口),将两个类的方法组合成一个类,并添加一些管理代码。 换句话说 假设我有这个界面: public interface ITestClass { string NamePlusLastName(string name); int AgePlus20(int age); DateTime YearYouWillDie(int age); } 我有两个类来实现它: public class TestClassPe

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

假设我有这个界面:

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");
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;
    }
}
而幕后发生的事情实际上是:

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

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

只需定义调用两个实现的第三个类,以某种方式组合它们的值并返回结果,实现接口本身。我不认为有必要同时进行测试,除非您想要动态地构建代码,这是一种完全不同的方式,而且要困难得多。我会这样做:

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

// call a method
string fullName = combinedClass.NamePlusLastName("William");
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;
    }
}

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

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

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

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作为构造函数的参数)

下面是调用处理程序代码。通过一些反射,我得到了被调用方法的名称及其参数。使用全局personChoice参数,我可以告诉这个处理程序实例化哪些类,然后决定(仲裁)调用哪些方法。对于这个例子,对于“两者”选项,我只是决定将返回值相加

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; }
}
最后,这里是程序的输出:

人名:彼得姓:彼得麦克年龄:55

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


人名:哈特盖特姓氏:哈特盖特Mc.Cormick哈特盖特冈萨雷斯年龄:528。。。但可能有一些基本的设计选择需要重新思考。“你能谈谈这个目标吗?”迈克尔谢谢你的回答。基本上,我们正在从一个数据存储过渡到另一个数据存储。甚至可能需要再加上第三个。因此,我们的系统需要能够一次无缝地写入所有数据。我们有一个定义存储库方法的接口。我们希望有一个类,该类将自动组合写入所有存储的方法,并放置一些仲裁代码。(示例从该数据库读取,但不是从该数据库读取)。我们不希望在组合类上显式实现每个方法。我们需要自动完成。谢谢您的回复。问题是我不想显式地实现这些方法。我需要自动完成。我在考虑使用反射,但不确定如何使用,我想知道是否有人已经这样做了。为什么它是自动的很重要?我不认为有一个简单的方法来做到这一点,甚至它也有问题的“联合”运作。如何实现自动化?您需要定义这是什么,如果是逐案的,那么与手动实现相比没有任何优势。它应该如何处理返回值?它应该按特定顺序调用派生方法吗?例外情况如何?我认为只滚动一个基本类并在那里进行操作要比使用复杂的反射容易得多。调试起来更容易,编写起来也更简单。问题是在我想要的应用程序中,我会有太多的方法。我将通过接口中的属性来指定行为。我将使用枚举作为参数来决定调用什么。
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));
    }
}