长话短说
假设我有以下代码:
// a class like this
class FirstObject {
public Object OneProperty {
get;
set;
}
// (other properties)
public Object OneMethod() {
// logic
}
}
// and another class with properties and methods names
// which are similar or exact the same if needed
class SecondObject {
public Object OneProperty {
get;
set;
}
// (other properties)
public Object OneMethod(String canHaveParameters) {
// logic
}
}
// the consuming code would be something like this
public static void main(String[] args) {
FirstObject myObject=new FirstObject();
// Use its properties and methods
Console.WriteLine("FirstObject.OneProperty value: "+myObject.OneProperty);
Console.WriteLine("FirstObject.OneMethod returned value: "+myObject.OneMethod());
// Now, for some reason, continue to use the
// same object but with another type
// -----> CHANGE FirstObject to SecondObject HERE <-----
// Continue to use properties and methods but
// this time calls were being made to SecondObject properties and Methods
Console.WriteLine("SecondObject.OneProperty value: "+myObject.OneProperty);
Console.WriteLine("SecondObject.OneMethod returned value: "+myObject.OneMethod(oneParameter));
}
是否可以将FirstObject
类型更改为SecondObject
并继续使用其属性和方法?
我完全控制了FirstObject
,但SecondObject
是密封的,完全超出了我的范围!
我可以通过反思来实现这一点吗?怎样你认为做这件事可能需要做什么工作?显然,这两个类都可能比上面的例子复杂得多。
这两个类都可以有FirstObject<T>
和SecondObject<T>
这样的模板,这让我不敢在这样的任务中使用反射!
现实中的问题
为了简单起见,我试图用更简单的方式陈述我的问题,并试图提取一些知识来解决它,但通过寻找答案,我似乎很明显,为了帮助我,你需要了解我的真正问题,因为改变对象类型只是冰山一角。
我正在开发工作流定义API。主要目标是在我可能想要使用的任何引擎(CLR通过WF4、NetBPM等(上拥有一个能够可重复使用的API
到目前为止,我正在编写中间层,以将API转换为WF4,从而通过CLR运行工作流。
我已经完成的
在这个阶段,API概念在某种程度上类似于带有
ActivityStates
的WF4,带有使用其参数通过ActivityStates
运行的输入/输出Arguments
和Data
(Variables
(。伪代码:中非常简化的API
class Argument { object Value; } class Data { String Name; Type ValueType; object Value; } class ActivityState { String DescriptiveName; } class MyIf: ActivityState { InArgument Condition; ActivityState Then; ActivityState Else; } class MySequence: ActivityState { Collection<Data> Data; Collection<ActivityState> Activities; }
我最初将其转换为WF4的方法也是通过
ActivitiesStates
图运行,并以某种方式直接分配属性,在需要的地方使用反射。再次简化伪代码,类似于:
new Activities.If() { DisplayName=myIf.DescriptiveName, Condition=TranslateArgumentTo_WF4_Argument(myIf.Condition), Then=TranslateActivityStateTo_WF4_Activity(myIf.Then), Else=TranslateActivityStateTo_WF4_Activity(myIf.Else) } new Activities.Sequence() { DisplayName=mySequence.DescriptiveName, Variables=TranslateDataTo_WF4_Variables(mySequence.Variables), Activities=TranslateActivitiesStatesTo_WF4_Activities(mySequence.Activities) }
在翻译结束时,我会有一个可执行的
System.Activities.Activity
对象。我已经轻而易举地做到了。大问题
当我开始将
Data
对象转换为System.Activities.Variable
时,这种方法出现了一个大问题。问题是WF4将工作流执行与上下文分离。因此,Arguments
和Variables
都是LocationReferences
,必须通过var.Get(context)
函数才能让引擎知道它们在运行时的位置。使用WF4:可以很容易地完成类似的操作
Variable<string> var1=new Variable<string>("varname1", "string value"); Variable<int> var2=new Variable<int>("varname2", 123); return new Sequence { Name="Sequence Activity", Variables=new Collection<Variable> { var1, var2 }, Activities=new Collection<Activity>(){ new Write() { Name="WriteActivity1", Text=new InArgument<string>( context => String.Format("String value: {0}", var1.Get(context))) }, new Write() { //Name = "WriteActivity2", Text=new InArgument<string>( context => String.Format("Int value: {0}", var2.Get(context))) } } };
但是如果我想通过我的API表示相同的工作流程:
Data<string> var1=new Data<string>("varname1", "string value"); Data<int> var2=new Data<int>("varname2", 123); return new Sequence() { DescriptiveName="Sequence Activity", Data=new Collection<Data> { var1, var2 }, Activities=new Collection<ActivityState>(){ new Write() { DescriptiveName="WriteActivity1", Text="String value: "+var1 // <-- BIG PROBLEM !! }, new Write() { DescriptiveName="WriteActivity2", Text="Int value: "+Convert.ToInt32(var2) // ANOTHER BIG PROBLEM !! } } };
当使用
Data
对象作为Variable
s时,我最终会遇到一个大问题。我真的不知道如何允许开发人员使用我的API,在任何需要的地方使用Data
对象(就像在WF4中一样(,然后将Data
转换为System.Activities.Variable
。
脑海中浮现的解决方案
如果您现在理解了我的问题,那么FirstObject
和SecondObject
分别是Data
和System.Activities.Variable
。正如我所说,将Data
翻译成Variable
只是冰山一角,因为我可能在代码中使用Data.Get()
,但在翻译时不知道如何将其翻译成Variable.Get(context)
。
我尝试过或想过的解决方案:
解决方案1
我将为每个流控制活动(
If
、Sequence
、Switch
…(开发NativeActivites
,并使用CacheMetadata()
函数指定Arguments
和Variables
,而不是直接转换属性。问题仍然存在,因为它们都是通过var.Get(context)
访问的。解决方案2
给我的
Data
类赋予它自己的Get()
函数。这只是一个抽象的方法,里面没有逻辑,它会以某种方式转换为System.Activities.Variable
的Get()
函数。这甚至可以使用C#吗?我猜不会!另一个问题是Variable.Get()
具有一个参数。解决方案3
我想到的最坏的解决方案是
CIL-manipulation
。尝试将使用Data/Argument
的代码替换为Variable/Argument
代码。这对我来说就像一场噩梦。我对System.reflection.Emit
几乎一无所知,即使我学会了,我猜这也需要很长时间。。。甚至不可能做到。
很抱歉,如果我最终引入了一个更大的问题,但我真的被困在这里,迫切需要一个提示/路径来继续。
这被称为"鸭子打字"(如果它看起来像鸭子,嘎嘎作响,你可以像鸭子一样调用它的方法(。将myObject声明为动态的,而不是特定的类型,这样就可以开始了。
编辑:为了清楚起见,这需要.NET 4.0
dynamic myObject = new FirstObject();
// do stuff
myObject = new SecondObject();
// do stuff again
反射不一定是正确的任务。如果SecondObject
超出了您的控制范围,您的最佳选择可能只是创建一个扩展方法,该方法实例化它的新副本,并逐个属性地跨数据进行复制。
您可以在复制过程中使用反射,并以这种方式工作,但这实际上是一个单独的问题。