Короче говоря
Скажите, у меня есть следующий код:
// 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
с In / Out 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
объектов в качестве Variable
с. Я действительно не знаю, как позволить разработчику, используя мой API, использовать Data
объекты где угодно, как в WF4, а затем перевести это Data
в System.Activities.Variable
.
Решения приходят на ум
Если вы теперь понимаете мою проблему, FirstObject
и SecondObject
- это Data
и System.Activities.Variable
соответственно . Как я уже сказал, перевод Data
в Variable
- это только верхушка айсберга, потому что я могу использовать Data.Get()
в своем коде и не знаю, как перевести его на Variable.Get(context)
во время перевода.
Решения, которые я пробовал или думал:
Решение 1
Вместо прямого перевода свойств я бы разработал NativeActivites
для каждого действия по управлению потоком (If
, Sequence
, Switch
, ...) и использовал бы функцию CacheMetadata()
для указания Arguments
и Variables
. Проблема остается, потому что они оба доступны через var.Get(context)
.
Решение 2
Дайте моему Data
классу собственную функцию Get()
. Это был бы только абстрактный метод, без логики внутри, который он каким-то образом перевел бы в Get()
функцию System.Activities.Variable
. Возможно ли это даже с помощью C #? Думаю нет! Другая проблема заключается в том, что Variable.Get()
имеет один параметр.
Решение 3
худшее решение , о котором я думал, было CIL-manipulation
.Попробуйте заменить код, где Data/Argument
используется с кодом Variable/Argument
.Это пахнет как кошмар для меня.Я почти ничего не знаю о System.reflection.Emit
, и даже если я узнаю это, я предполагаю, что это заняло бы целую вечность ... и, возможно, даже было бы невозможно сделать это.
Извините, если я закончил тем, что представил большую проблему, но я действительно застрял здесь и отчаянно нуждаюсь в подсказке / пути, чтобы продолжить.