长话短说
假设我有以下代码:
// 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的概念在某种程度上类似于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的方法也在
ActivityStates
图中运行,并以某种方式直接分配属性,在需要时使用反射。再次简化伪代码,类似于:
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)
}
在翻译结束时,我会有一个可执行的
ActivitiesStates
对象。我已经很容易做到了。大问题
当我开始将
System.Activities.Activity
对象转换为Data
时,这种方法出现了一个大问题。问题是wf4将工作流执行与上下文分离。因为System.Activities.Variable
和Arguments
都是Variables
必须通过LocationReferences
函数访问,以便引擎知道它们在运行时的位置。使用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 !!
}
}
};
当使用
var.Get(context)
对象作为Data
s时,我遇到了一个大问题。我真的不知道如何允许开发人员使用我的api,在任何需要的地方使用Variable
对象(就像在wf4中一样),然后将其转换为Data
。想到了解决办法
如果你现在理解我的问题,
Data
和System.Activities.Variable
分别是FirstObject
和SecondObject
。就像我说的,translateData
toSystem.Activities.Variable
只是冰山一角,因为我可能会在代码中使用Data
,而且在进行翻译时不知道如何将其转换为Variable
。我尝试过或考虑过的解决方案:
解决方案1
我将为每个流量控制活动(
Data.Get()
,Variable.Get(context)
,NativeActivites
,If
,…)开发Sequence
属性,并使用Switch
函数指定CacheMetadata()
和Arguments
。问题仍然存在,因为它们都是通过Variables
访问的。解决方案2
给我的
var.Get(context)
类自己的Data
函数。它只是一个抽象的方法,里面没有逻辑,不知何故,它会转换成Get()
的Get()
函数。使用c_有可能吗?我猜不是!另一个问题是aSystem.Activities.Variable
有一个参数。解决方案3
我想到的最糟糕的解决方案是
Variable.Get()
。尝试用CIL-manipulation
代码替换Data/Argument
使用的代码。这闻起来像个噩梦。我对Variable/Argument
几乎一无所知,即使我知道它,我的猜测是它将需要很长时间…甚至不可能做到。很抱歉,如果我引入了一个更大的问题,但我真的困在这里,迫切需要一个提示/路径继续下去。
最佳答案
这被称为“duck-typing”(如果它看起来像一只鸭子,并且嘎嘎嘎嘎地叫起来像鸭子一样,那么您可以对它调用方法,就好像它真的是一只鸭子一样)。将myobject声明为动态的,而不是特定的类型,然后就可以开始了。
编辑:为了清楚起见,这需要.NET 4.0dynamic myObject = new FirstObject();
// do stuff
myObject = new SecondObject();
// do stuff again
关于c# - 在运行时更改对象类型,维护功能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5671146/