有没有办法创建一些代码,编译器将生成一些像下面的其他代码?

时间:2013-12-11 08:56:31

标签: c# reflection

假设我在c#语言中有这种类:

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,在阶乘源代码中。

6 个答案:

答案 0 :(得分: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 Expressions

构建方法
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()的结果作为动态调用的方法。

答案 1 :(得分:1)

T4模板

T4 Template可以生成所需的C#代码,稍后会编译成IL代码,就像您自己编写了代码一样。如果您想使用此技术,最自然的方法是使用partial classes。第一个partial定义了除自动生成方法之外的所有类。第二部分将由简单的T4模板生成。 (在编译的代码中,单个文件中定义的类或多个部分中的类没有区别。)

<强> Reflection.Emit的

如果你真的想在运行时生成代码,那就更难了,但是你可以使用Reflection.Emit来实现它。这允许在运行时直接发出IL。

表达式树

这也允许在运行时生成和编译代码。它比第二种选择更容易。 See an introudction here

<强>反射

如果您想使用原始的Reflection解决方案,您应该将FieldInfo存储在静态结构(数组,列表,字典或其他)中,这样您只需要反映字段一次的开销。这将改善表现。

选择什么

除非有充分的理由不这样做,否则我更喜欢T4模板。它更容易实现,并且让编译器具有编译和优化代码的可靠性。除此之外,您不必使用“模糊,不寻常”的概念。

总的来说,我不会建议你第二种选择。在其他事情之间,我认为这需要完全信任。而且,你需要很好地了解自己在做什么。您还会错过编译器优化。

使用表达式树并不像使用Reflection.Emit那么难,但它仍然很难做到。

反射总是增加一点开销,特别是如果你不缓存FieldInfo s(或PropertyInfo s或其他)。我会留下它的唯一解决方案。例如,检查属性是否存在或从ouside访问类的私有或受保护成员。

答案 2 :(得分:0)

您可以使用字典来映射ID:

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

答案 3 :(得分:0)

您可以更改代码中的一些细节吗?如果将整数定义为一个包含100个元素的数组,则只需使用id作为索引并返回:

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

如果你真的需要从外部访问这些值(它们被定义为public?),你可以使用一个首选公共整数的属性来公开它们。

答案 4 :(得分:0)

我没有使用过它们,但我知道Visual Studio附带了T4 Text Templates,它可以满足您的需求。

答案 5 :(得分: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]();