【发布时间】:2011-08-05 23:34:12
【问题描述】:
长话短说
假设我有以下代码:
// 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。主要目标是让 API 能够在我可能想要使用的任何引擎(CLR 到 WF4、NetBPM 等)之上可重用。
现在我正在编写中间层来将该 API 转换为 WF4 以通过 CLR 运行工作流。
-
我已经完成的事情
在这个阶段,API 概念在某种程度上类似于 WF4,其中
ActivityStates带有输入/输出Arguments和Data(Variables) 使用它们的参数运行通过ActivityStates。伪代码中非常简化的 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对象作为Variables 时,我最终遇到了一个大问题。我真的不知道如何允许开发人员使用我的 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()函数来指定@ 987654363@ 和Variables。问题仍然存在,因为它们都是通过var.Get(context)访问的。 -
解决方案 2
给我的
Data类提供自己的Get()函数。它只是一个抽象方法,没有内部逻辑,它会以某种方式转换为System.Activities.Variable的Get()函数。这甚至可以使用 C# 吗?可能不会!另一个问题是Variable.Get()有一个参数。 -
解决方案 3
我想到的最糟糕的解决方案是
CIL-manipulation。尝试将使用Data/Argument的代码替换为Variable/Argument代码。这对我来说就像一场噩梦。我对System.reflection.Emit几乎一无所知,即使我学会了它,我的猜测是它需要很长时间......甚至可能无法做到。
很抱歉,如果我最终引入了一个更大的问题,但我真的被困在这里并且迫切需要一个提示/路径继续下去。
【问题讨论】:
-
你到底想用这个来完成什么?您是否尝试以某种方式或其他方式扩展 FirstObject 的功能?
-
@craig-suchanec 这是一个相当大的文本,但就像你说的,我真的需要深入解释它。恐怕我最初的问题只是冰山一角。
-
您可能应该将此作为 WF4 问题重新发布,以便正确的专家看到它
-
已经做到了,并为此赢得了拇指草袋:\ 没有一条评论。我猜是因为这与其说是 WF4 问题,不如说是编程方法的问题。
标签: c# reflection types runtime