2013-12-11 1 views
0

Пусть у меня есть такой класс в языке С #:Есть ли способ создать некоторые коды, которые компилятор будет генерировать еще один код, как показано ниже?

public class ABC { 
    public int var_1; 
    public int var_2; 
    public int var_3; 
    //... until 100 
    public int var_100; 


    public int GetData_WithBasicIfElse (int id) { 
     if(id == 1) 
      return var_1; 
     else if(id == 2) 
      return var_2; 
     else //and so on until 
     else if(id == 100) 
      return var_100; 
    } 

    public int GetData_WithReflection(int id){ 
     string key = "var_" + id.ToString(); 
     FieldInfo info = GetType().GetField (key); 
     return info != null ? (int)info.GetValue (this) : 0; 
    } 

    public int GetData_WithSpecialCode(int id){ 
     //put the simple codes here, then compilers compile it, it will generate code like the method  GetData_WithBasicIfElse 
    } 
} 

На самом деле в большинстве случаев, я могу использовать массив для хранения var_n переменного, но я просто любопытен, если есть другой способ. Я не хочу использовать GetData_WithBasicIfElse (не изящно), но мне интересно, есть ли другое решение помимо использования отражения.

То, что я имею в виду с GetData_WithSpecialCode, содержит специальный код, который будет преобразован компилятором (когда время компиляции, где будет бинарный файл), в некоторый шаблон, например GetData_WithBasicIfElse.

ОБНОВЛЕНО Эта методика под названием шаблона метапрограммирование, как вы можете увидеть здесь: http://en.wikipedia.org/wiki/Template_metaprogramming, в исходном коде факториала.

+2

Что не так с отражением? –

+1

Если у вас есть 100 идентичных переменных (с точки зрения типа), и вы хотите получить к ним доступ по номеру индекса, мне остается недоумевать, почему вы не просто используете массив. –

+0

@ L.B: нет ничего плохого в отражении, мне просто любопытно, кажется, что есть код в c, который использует свой компилятор для генерации кода во время компиляции. Удивительно, если C# тоже имеет эту технику. –

ответ

1

T4 Шаблон

T4 Template может генерировать, что требуемый C# код, который в дальнейшем будет скомпилирован в IL код, как если бы вы написали, что код самостоятельно. Если вы хотите использовать эту технику, наиболее естественным способом является использование partial classes. Первый частичный определяет весь класс, кроме автогенерированного метода. Вторая часть будет сгенерирована простым шаблоном T4. (В скомпилированном коде нет разницы между классом, определенным в одном файле или несколькими частичными).

Reflection.Emit

Если вы действительно хотите, чтобы генерировать код во время выполнения, это гораздо сложнее сделать, но вы можете сделать это с помощью Reflection.Emit Это позволяет непосредственно испускают IL во время выполнения.

Деревья выражений

Это также позволяет создавать и компилировать код во время выполнения. Это проще, чем второй вариант. See an introudction here.

Отражение

Если вы хотите использовать оригинальное решение Reflection следует хранить FieldInfo S в статической структуре (массив, список, словарь или любой другой), так что у вас есть только накладные, отражающие поля один раз. Это улучшит производительность.

Что выбрать

Если не уважительная причина не делать этого, я бы предпочел шаблон T4. Это проще в реализации, и вы оставляете компилятор для обеспечения возможности компиляции и оптимизации вашего кода. кроме того, вам не нужно работать с «неясными, необычными» концепциями.

В общем, я бы не посоветовал вам второй вариант. Между прочим, я думаю, что это требует полного доверия. И вам нужно хорошее знание того, что вы делаете. Вы также пропустите оптимизацию компилятора.

Использование деревьев выражений не так сложно, как использование Reflection.Emit, но это все еще сложно сделать.

И отражение всегда добавляет немного накладных расходов, особенно если вы не кэшируете FieldInfo s (или PropertyInfo s или что-то еще). Я оставил бы его для случаев, где это единственное решение. Например, проверьте, существует ли свойство или доступ к частному или защищенному члену класса из ouside.

0

Вы можете использовать словарь для отображения идентификаторов:

public class ABC 
{ 
    public int var_1; 
    public int var_2; 
    public int var_3; 
    //... until 100 
    public int var_100; 

    private Dictionary<int,int> map; 

    public ABC() 
    { 
     //build up the mapping 
     map = new Dictionary<int,int>(); 
     map.Add(1,var_1); 
     map.Add(2,var_2); 
     map.Add(100,var_100); 
    } 

    public int GetData(int id) 
    { 
     //maybe here you need to do check if the key is present 
     return map[id]; 
    } 
} 
+0

Так как 'int' - тип значения, что произойдет, когда поля изменят значение? – Jodrell

+1

Для каких полей?(вероятно, лучше избавиться от них) - 'int' - тип значения, поэтому с помощью вышеупомянутого решения' var_1 = 44' будет ** не ** изменять значение 'map [1]' – Justin

0

Вы можете изменить некоторые детали в вашем коде? Если определить целые числа в качестве одного из массива с 100 элементами вы можете просто использовать идентификатор в качестве индекса и возвращает этот:

public int GetData_WithSpecialCode(int id){ 
    return var_array(id) 
} 

Если вам действительно нужно получить доступ к значениям снаружи (они определены общественности?) Вы можете выставить они используют свойство, которое является предпочтительным для публичных целых чисел.

0

Я не использовал их, но я знаю, Visual Studio поставляется с T4 Text Templates, которые могут делать то, что вам нужно.

0

Конечно,

switch (id) 
{ 
    case 1: 
     return this.var_1; 
    case 2: 
     return this.var_2; 
    // etc. etc. 
} 

или

var lookup = new Dictionary<int, Func<int>> 
    { 
     { 1,() => return this.var_1 }, 
     { 2,() => return this.var_2 }, 
     // etc. etc. 
    }; 

return lookup[i](); 
1

Мне очень интересно, почему вы не можете использовать массив. Конечно, использование какого-то словаря было бы лучше. Но верить вам на самом деле не может, у вас есть по крайней мере два варианта для генерации такого метода:

1) CSharpCodeProvider

Вы можете построить строку с вспомогательным классом, содержащей ваш метод, и он будет составлен к другой сборке:

string source = "public class Description" + 
       "{" + 
       " public int GetData_WithBasicIfElse(int id) {" + 
       // ... all ifs generated here 
       " }" + 
       "}"; 

CSharpCodeProvider codeProvider = new CSharpCodeProvider(); 
System.CodeDom.Compiler.CompilerParameters parameters = new CompilerParameters(); 
parameters.GenerateExecutable = false; 
parameters.GenerateInMemory = true; 
CompilerResults result = codeProvider.CompileAssemblyFromSource(parameters, source); 
if (!result.Errors.HasErrors) 
{ 
    Type type = result.CompiledAssembly.GetType("Description"); 
    var instance = Activator.CreateInstance(type); 
} 

и теперь у вас есть instance хелперов класса

2) Linq Expressions

Вы можете создать метод с использованием Linq выражений

ParameterExpression id = Expression.Parameter(typeof(int), "id"); 
List<Expression> expressions = new List<Expression>(); 

// here a lot of adding if-else statement expressions 
expressions.Add(...); 

var lambda = Expression.Lambda(
    Expression.Block(
     expressions 
    ), 
    id); 

Затем вы можете использовать результат lambda.Compile() как метод для вызова динамически.

+0

@AgungPratama, если вы начиная с варианта 1, вы можете динамически испускать тип, который создает необходимые свойства с помощью аксессуаров, которые получают из массива. – Jodrell

Смежные вопросы