c# - 在运行时更改对象类型,维护功能

标签 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中运行ArgumentsDataVariables)。
非常简化的伪代码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.VariableArguments都是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)对象作为Datas时,我遇到了一个大问题。我真的不知道如何允许开发人员使用我的api,在任何需要的地方使用Variable对象(就像在wf4中一样),然后将其转换为Data
想到了解决办法
如果你现在理解我的问题,DataSystem.Activities.Variable分别是FirstObjectSecondObject。就像我说的,translateDatatoSystem.Activities.Variable只是冰山一角,因为我可能会在代码中使用Data,而且在进行翻译时不知道如何将其转换为Variable
我尝试过或考虑过的解决方案:
解决方案1
我将为每个流量控制活动(Data.Get()Variable.Get(context)NativeActivitesIf,…)开发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.0
dynamic myObject = new FirstObject();
// do stuff
myObject = new SecondObject();
// do stuff again

关于c# - 在运行时更改对象类型,维护功能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5671146/

相关文章:

java - 我可以在没有注释名称的情况下获取注释的任何值吗?

c# - 反射(reflection):FindMembers 返回空

c# - 如何打开 Internet Explorer 窗口,在 url 上导航并使用 c# 和 mshtml 库获取她的文档(HTMLDocument 或 InternetExplorer)

c# - 在单元测试项目中获取具有属性的程序集

c# - 从 web 服务填充后将项目添加到 Telerik Ajax RadComboBox

java - 如何使用反射(Java)调用私有(private)静态方法?

c++ - 通用函数指针

javascript - TypeScript 正在使用不同版本的@types 进行 react

c# - 字段初始化后无法从主线程访问对象

c# - 无法安装 Newtonsoft Json