在运行时维护功能更改对象类型

时间:2011-04-15 00:19:12

标签: c# reflection types runtime

长话短说

说我有以下代码:

// 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包含In / Out ArgumentsDataVariables)贯穿{{1使用他们的参数。

    伪代码中的非常简化的API

    ActivityStates

    我最初将此转换为WF4的方法是在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; } 图表中运行,并以某种方式直接分配属性,在需要时使用反射。

    再次简化伪代码,类似于:

    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翻译时,出现了这种方法的一个大问题。问题是WF4将工作流程执行与上下文分开。因为System.Activities.VariableArguments都是Variables,必须通过LocationReferences函数访问,以便引擎知道它们在运行时的位置。

    使用WF4很容易实现这样的事情:

    var.Get(context)

    但如果我想通过我的API表示相同的工作流程:

    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)))
            }
        }
    };
    

    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时,我最终遇到了 BIG PROBLEM 。我真的不知道如何允许开发人员使用我的API在任何想要的地方使用Variable个对象(就像在WF4中一样),然后将Data转换为Data


想到解决方案

如果您现在了解我的问题,System.Activities.VariableFirstObject分别是SecondObjectData 。就像我说翻译System.Activities.VariableData只是冰山一角,因为我可能会在我的代码中使用Variable而不知道如何将其翻译为Data.Get()做翻译。

我尝试或想过的解决方案:

  • 解决方案1 ​​

    我不会直接翻译属性,而是为每个流量控制活动(Variable.Get(context)NativeActivitesIf,...)开发Sequence并使用Switch函数指定CacheMetadata()Arguments。问题仍然存在,因为它们都是通过Variables访问的。

  • 解决方案2

    为我的var.Get(context)课程提供自己的Data功能。它只是一个抽象的方法,没有内部逻辑,它会以某种方式转换为Get()的{​​{1}}函数。这甚至可以使用C#吗?可能不会!另一个问题是Get()有一个参数。

  • 解决方案3

    我想到的最差解决方案System.Activities.Variable。尝试使用Variable.Get()代码替换CIL-manipulation使用的代码。这对我来说就像是一场噩梦。我对Data/Argument几乎一无所知,即使我了解它,我的猜测是它需要很长时间......甚至可能无法做到。

很抱歉,如果我最终引入了一个更大的问题,但我真的被困在这里,迫切需要一个提示/路径继续。

2 个答案:

答案 0 :(得分:8)

这被称为“鸭子打字”(如果它看起来像鸭子和像鸭子一样嘎嘎叫,你可以在它上面调用方法,好像它真的是一只鸭子)。将myObject声明为动态而不是特定类型,然后您应该好好去。

编辑:要明确,这需要.NET 4.0

dynamic myObject = new FirstObject();

// do stuff

myObject = new SecondObject();

// do stuff again

答案 1 :(得分:3)

反思不一定是正确的任务。如果SecondObject超出您的控制范围,那么您最好的选择可能只是制作一个扩展方法来实例化它的新副本并按属性复制数据属性。

您可以将反射用于复制过程,并以这种方式工作,但这确实是一个单独的问题。