好的,经过一些研究和测试,我想我找到了一个可行的解决方案。它需要使用 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