长话短说
假设我有以下代码:
// 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
是 sealed 的,完全超出了我的范围!
我是否可以通过反射实现这一点?如何实现?您认为需要多少工作量才能完成此操作?显然,两个类都可以比上面的示例复杂得多。
两个类都可以具有像 FirstObject<T>
和 SecondObject<T>
这样的模板,这让我想使用反射来完成这样的任务感到害怕!
现实问题
为了简单起见并尝试提取一些解决问题的知识,我已经尽力陈述我的问题,但通过查看答案,我认为您需要了解我的真正问题来帮助我,因为更改对象类型只是冰山一角。
我正在开发一个工作流定义API。主要目标是拥有一个能够在任何我想使用的引擎之上进行可重复使用的API(通过WF4、NetBPM等CLR)。
现在,我正在编写中间层,将该API转换为WF4以通过CLR运行工作流程。
我已经完成的工作
在这个阶段,API的概念与WF4中的
ActivityStates
与输入/输出Arguments
和通过其参数运行的Data
(Variables
)相似。极简化的伪代码: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
时,我遇到了一个大问题。 我真的不知道如何允许开发人员在我
解决方案浮现
如果您现在理解我的问题,
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
。尝试用Variable/Argument
代码替换使用Data/Argument
的代码。这对我来说就像噩梦一样。我对System.reflection.Emit
几乎一无所知,即使我学会了,我的猜测是这可能需要很长时间...甚至可能无法完成。
抱歉如果我引入了更大的问题,但我真的陷入困境了,需要一个提示/路径。