假设我在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,在阶乘源代码中。
答案 0 :(得分:1)
我真的好奇为什么你不能使用数组。肯定使用某种字典会更好。但是相信你真的不能,你至少有两个选择来产生这样一个方法:
您可以使用包含方法的帮助程序类构建一个字符串,并将其编译为另一个程序集:
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
辅助类
您可以使用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]();