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

Joa*_*oao 4 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));
}
Run Code Online (Sandbox Code Playgroud)

是否可以将FirstObject类型更改为SecondObject并继续使用它的属性和方法?

我已经超过总量控制FirstObject,但SecondObject密封,完全从我的范围!

我可以通过反思实现这一目标吗 怎么样?您如何看待它可能需要做的工作?显然,这两个类都比上面的例子复杂得多.

这两个类都可以有类似的模板FirstObject<T>,SecondObject<T>这让我很难使用反射来完成这样的任务!


现实中的问题

为了简单起见,我试图说明我的问题更简单,并尝试提取一些知识来解决它,但是,通过寻找答案,我觉得很明显,为了帮助我,你需要了解我的真正的问题,因为改变对象类型只是冰山一角.

我正在开发一个Workflow Definition API.主要目标是使API能够在我可能想要使用的任何引擎(CLR到WF4,NetBPM等)之上重复使用.

到现在为止,我正在编写中间层,将该API转换为WF4,以便通过CLR运行工作流程.

  • 我已经完成了什么

    在这个阶段,API概念在某种程度上类似于WF4,ActivityStates其中In/Out ArgumentsData(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;
    }
    
    Run Code Online (Sandbox Code Playgroud)

    我最初将其转换为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)
    }
    
    Run Code Online (Sandbox Code Playgroud)

    在翻译结束时,我将有一个可执行System.Activities.Activity对象.我已经轻松完成了这项工作.

  • 最大的问题

    当我开始翻译Data对象时,出现了这种方法的一个大问题System.Activities.Variable.问题是WF4将工作流程执行与上下文分开.因为这两者ArgumentsVariablesLocationReferences必须通过访问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)))
            }
        }
    };
    
    Run Code Online (Sandbox Code Playgroud)

    但如果我想通过我的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 !!
            }
        }
    };
    
    Run Code Online (Sandbox Code Playgroud)

    当使用对象作为s 时,我最终遇到了一个大问题.我真的不知道如何允许开发人员使用我的API 在任何想要的地方使用对象(就像在WF4中一样),然后将其转换为.DataVariableDataDataSystem.Activities.Variable


想到解决方案

如果你现在明白我的问题,FirstObject并且SecondObjectDataSystem.Activities.Variable 分别.就像我说的翻译DataVariable是冰山的一角,因为我可能会用Data.Get()在我的代码,不知道如何把它翻译成Variable.Get(context)一边做翻译.

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

  • 解决方案1

    相反性质的直接翻译过来的我会发展NativeActivites为每个流控制活动(If,Sequence,Switch,...)和使用CacheMetadata()功能来指定ArgumentsVariables.问题仍然存在,因为它们都是通过访问var.Get(context).

  • 解决方案2

    给我的Data班级自己的Get()功能.它只是一个抽象的方法,没有内部逻辑,它会以某种方式转换为Get()函数System.Activities.Variable.这甚至可以使用C#吗?可能不会!另一个问题是a Variable.Get()有一个参数.

  • 解决方案3

    最坏的解决方案,我认为是的CIL-manipulation.尝试将代码中Data/Argument使用的代码替换为Variable/Argument代码.这对我来说就像是一场噩梦.我几乎一无所知System.reflection.Emit,即使我学习它,我的猜测是它需要很长时间......甚至可能都不可能做到.

对不起,如果我最终引入了一个更大的问题,但我真的被困在这里,迫切需要一个提示/路径继续下去.

Rob*_*evy 8

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

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

dynamic myObject = new FirstObject();

// do stuff

myObject = new SecondObject();

// do stuff again