Reputation: 3784
Let say I have this kind of class in c# language:
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
}
}
Actually in most cases, I can use the array to hold var_n variable, but I am just curious if there is another way. I do not want to use GetData_WithBasicIfElse
(not elegant), but I am wondering if there is another solution beside using reflection.
What I mean with GetData_WithSpecialCode
is, it contains the special code that will be transformed by compiler (when compile time, where it will be binary file) into some pattern like GetData_WithBasicIfElse
.
UPDATED This technique's called Template metaprogramming, as you can see in here: http://en.wikipedia.org/wiki/Template_metaprogramming, in the factorial source code.
Upvotes: 0
Views: 94
Reputation: 39025
T4 Template
A T4 Template can generate that desired C# code, that will be later compiled into IL code, as if you have written that code yourself. If you want to use this technique, the most natural way is to use partial classes. The first partial defines all the class except the auto-generated method. The second partial would be generated by a simple T4 template. (In the compiled code there's no difference between a class defined in a single file or in several partials).
Reflection.Emit
If you really want to generate code at runtime, it's much harder to do, but you can do it using Reflection.Emit This allow to directly emit IL at run time.
Expression Trees
This also allows to generate and compile code at run time. It's easier than the second option. See an introudction here.
Reflection
If you want to use your original Reflection solution you should store the FieldInfo
s in an static structure (array, list, dictionary or whatever) so that you only have the overhead of reflecting the fields once. This will improve the performace.
What to choose
Unless there is a good reason not to do so, I'd prefer the T4 template. It's the easier to implement, and you leave the compiler the reponsibility to compile and optimize your code. besides you don't have to work with "obscure, unusual" concepts.
In general I wouldn't advice you the second option. Between other things, I think this requires full trust. And, you need a good knowledge of what you're doing. You also miss the compiler optimizations.
Using expression trees is not as hard as using Reflection.Emit, but it's still hard to do.
And reflection always add a little overhead, specially if you don't cache the FieldInfo
s (or PropertyInfo
s or whatever). I would leave it for cases where is the only solution. For example checking if a property exists or accessing a private or protected member of a class from ouside.
Upvotes: 1
Reputation: 16878
I really wonder why you can't use array. For sure using some kind of dictionary would be better. But believing you really can't, you have at least two options to generate such a method:
You can build a string with helper class containing your method, and it will be compiled to a different assembly:
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);
}
and now you have an instance
of helper class
You can build a method using 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);
Then you can use a result of lambda.Compile() as a method to call dynamically.
Upvotes: 1
Reputation: 35716
Of course,
switch (id)
{
case 1:
return this.var_1;
case 2:
return this.var_2;
// etc. etc.
}
or,
var lookup = new Dictionary<int, Func<int>>
{
{ 1, () => return this.var_1 },
{ 2, () => return this.var_2 },
// etc. etc.
};
return lookup[i]();
Upvotes: 0
Reputation: 210505
I haven't used them, but I know Visual Studio comes with T4 Text Templates that may do what you need.
Upvotes: 0
Reputation: 3701
Can you change some details in your code? If you define the integers as one Array with 100 elements you can simply use id as an index and return that:
public int GetData_WithSpecialCode(int id){
return var_array(id)
}
If you really need to access the values from outside (they are defined public?) you can expose them using a property wich is preferred to public integers.
Upvotes: 0
Reputation: 15941
You can use a dictionary to map the ids:
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];
}
}
Upvotes: 0