I have an object that provides several functions to write and read data from a packet, something like this:
class Packet
{
void Write(int value) {/*...*/}
//...
int ReadInt() {/*...*/}
bool ReadBool() {/*...*/}
string ReadString() {/*...*/}
Vector3 ReadVector3() {/*...*/}
}
this class stores a byte[] that is sent over the network. If I want to access data previously written, I can do it like this
void MyFunction(Packet packet)
{
int myInt = packet.ReadInt();
bool myBool = packet.ReadBool();
string myString = packet.ReadString();
Vector3 myVector3 = packet.ReadVector3();
//... do stuff
}
I was wondering if there is some syntactic sugar that would allow me to define a function taking a variable number of parameters of different types, detect which dynamic types they are at runtime, call the appropriate function and then return the parameter, something like this:
class Packet
{
//...
void ReadAll(out params object[] objects);
}
void MyFunction(Packet packet)
{
packet.ReadAll(out int myInt, out bool myBool, out string myString, out Vector3 myVector3);
//... do stuff with myInt, myBool, myString, myVector3
}
I looked at params with an object[], the out keyword, generics, and Convert.ChangeType() but I couldn't get anything to work so far. I'm not sure this is even possible, and if it is, if the runtime cost of reflection will highly outweigh the benefits of simpler/less code for something used as frequently as network packets.
Thank you all.
You can try using generics and ValueTuples:
public T Read<T>() where T:ITuple
{
return default(T); // some magic to create and fill one
}
and usage:
var (i, j) = Read<(int, int)>();
// or
var x = Read<(int i, int j)>();
As for reflection - you can cache reflection "results" per type:
public T Read<T>() where T : struct, ITuple
{
return TupleCreator<T>.Create(new ValueReader());
}
static class TupleCreator<T> where T : struct, ITuple
{
private static Func<ValueReader, T> factory;
static TupleCreator()
{
var fieldTypes = typeof(T).GetFields()
.Select(fi => fi.FieldType)
.ToArray();
if(fieldTypes.Length > 7)
{
throw new Exception("TODO");
}
var createMethod = typeof(ValueTuple).GetMethods()
.Where(m => m.Name == "Create" && m.GetParameters().Length == fieldTypes.Length)
.SingleOrDefault() ?? throw new NotSupportedException("ValueTuple.Create method not found.");
var createGenericMethod = createMethod.MakeGenericMethod(fieldTypes);
var par = Expression.Parameter(typeof(ValueReader));
// you will need to figure out how to find your `read` methods
// but it should be easy - just find a method starting with "Read"
// And returning desired type
// I can do as simple as:
var readMethod = typeof(ValueReader).GetMethod("Read");
var readCalls = fieldTypes
.Select(t => readMethod.MakeGenericMethod(t)) // can cache here also but I think not needed to
.Select(m => Expression.Call(par, m));
var create = Expression.Call(createGenericMethod, readCalls);
factory = Expression.Lambda<Func<ValueReader, T>>(create, par).Compile();
}
public static T Create(ValueReader reader) => factory(reader);
}
class ValueReader
{
public T Read<T>()
{
return default; // to simulate your read
}
}
Console.WriteLine(Read<(int i, double j)>()); // prints "(0, 0)"
NB
Also you can just implement the Read<T> method with "caching" the reflection as done here.
Yes, Reflection is always having a cost of Performance, but it's not always so big, especially when you are not using reflective calls via big size collections or dealing with a very complicated code (here is a Microsoft official tutorial about when to use the Reflection).
Anyway, back to our business, using a reflection here is what I think might help you:
class Packet {
...
public T Read<T>()
{
var currentType = Type.GetType(this.GetType().ToString());
var methodInfo = currentType?
.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance) // Getting all "private" read methods
.FirstOrDefault(m =>
m.Name.Contains("Read") && m.ReturnType == typeof(T));
// here it is assuming that you will have to have only 1 Read method with the type int, bool, etc.
return (T) methodInfo?.Invoke(this, null);
}
}
public List<object> ReadAllAsCollection(params Type[] types)
{
var result = new List<object>(); // in order to hold various type you need to have a collection of `object` type elements
foreach (Type type in types)
{
MethodInfo method = typeof(Packet).GetMethod("Read");
MethodInfo genericMethod = method?.MakeGenericMethod(type);
var res = genericMethod?.Invoke(this, null); // calling Read<T> with appropriate type
result.Add(res);
}
return result;
}
public T ReadAllAsTuple<T>() where T : ITuple
{
T CreateValueTuple(List<object> items, Type[] inputTypes)
{
var methodInfo = typeof(ValueTuple)
.GetMethods()
.FirstOrDefault(m =>
m.Name.Contains("Create") &&
m.GetParameters().Length == items.Count);
var invokeResult = methodInfo?.MakeGenericMethod(inputTypes)
.Invoke(null, items.ToArray());
return (T)invokeResult;
}
var tupleType = typeof(T);
var types = tupleType.GetFields().Select(f => f.FieldType).ToArray();
var result = types
.Select(t =>
{
var method = typeof(Packet).GetMethod("Read");
var genericMethod = method?.MakeGenericMethod(t);
return genericMethod?.Invoke(this, null); // calling Read<T> with appropriate type
}).ToList();
return result.Any() ?
CreateValueTuple(result, types)
: default;
}
And a usage:
var p = new Packet();
var elm = p.Read<int>(); // single instance call, ReadInt() call is encapsulated
var resultAsCollection = p.ReadAllAsCollection(typeof(int), typeof(string));
var resultAsTuple = p.ReadAllAsTuple<ValueTuple<int, string, bool>>();
A few notes:
As you probably want a single exposed public method (Read), it would be good to have other methods declared as private.
In this case, you have simple method names, but to escape possible name conflicts, you can use some specific naming conventions, like: _ReadInt_() or any other namings you prefer. Though, this example will work even without specific namings.
It is assumed that you will have only one method with the name Read... and return type of T (for example int ReadInt()) because, for the above case, we are using only the first match.
Is it possible to build function at runtime in c#?
Say I have this method:
List<object> DoSomeWork(List<object> listOfItems, bool boolA, bool boolB)
{
var resultsList = new List<object>();
foreach (var value in listOfItems)
{
var resultOfWork = CallFunction(value);
if (boolA)
{
resultOfWork = AnotherFunctionCall(resultOfWork);
}
if (boolB)
{
resultOfWork = ThirdFunctionCall(resultOfWork);
}
resultsList.Add(resultOfWork);
}
return resultsList;
}
Is there a way I can dynamically build a function at runtime to prevent the need to check boolA and boolB for every iteration of the loop?
In my head I'd have something that looks like this:
List<object> DoSomeWork(List<object> listOfItems, bool boolA, bool boolB)
{
Func<object, object> processor = (toProcess) =>
{
var resultOfWork = CallFunction(toProcess);
}
if (boolA)
{
processor += { resultOfWork = AnotherFunctionCall(resultOfWork); };
}
if (boolB)
{
processor += { resultOfWork = ThirdFunctionCall(resultOfWork); };
}
processor += { return resultOfWork; };
var resultsList = new List<object>();
foreach (var value in listOfItems)
{
resultsList.Add(processor(value));
}
return resultsList;
}
Thanks in advance.
There are several ways of achieving what you wish to achieve. You can use Linq.Expression to construct a function at run-time. However, since you've got only two Boolean flags, you can take a simpler approach that uses four "pre-built" function objects, like this:
Func<object,object> processor;
if (boolA && boolB) {
processor = v => ThirdFunctionCall(AnotherFunctionCall(CallFunction(x)));
} else if (boolA && !boolB) {
processor = v => AnotherFunctionCall(CallFunction(x));
} else if (!boolA && boolB) {
processor = v => ThirdFunctionCall(CallFunction(x));
} else {
processor = v => CallFunction(v);
}
var resultsList = listOfItems.Select(processor).ToList();
The bulk of the logic is in the chain of if conditionals, which enumerate all four possibilities for the pair {boolA, boolB}, and assigns the processor functor a lambda expression with the appropriate functionality.
Please note the absence of the explicit foreach loop, which can be replaced now with a LINQ call of Select(...) followed by ToList().
(Updated)
This example builds and compiles a function that varies based on the results you passed in.
public static object CallFunction(object item) { return item; }
public static object AnotherFunctionCall(object item) { return item; }
public static object ThirdFunctionCall(object item) { return item; }
public static MethodInfo CallFunctionMethodInfo = typeof(BuildFunction).GetMethod("CallFunction");
public static MethodInfo AnotherFunctionCallMethodInfo = typeof(BuildFunction).GetMethod("AnotherFunctionCall");
public static MethodInfo ThirdFunctionCallMethodInfo = typeof(BuildFunction).GetMethod("ThirdFunctionCall");
public static Func<object, object> CreateFunc(bool boolA, bool boolB)
{
var objectParameter = Expression.Parameter(typeof(object));
var returnVar = Expression.Variable(typeof(object), "returnVar");
var commands = new List<Expression>();
commands.Add(
Expression.Assign(
returnVar,
Expression.Call(CallFunctionMethodInfo, objectParameter)));
if (boolA)
{
commands.Add(
Expression.Assign(
returnVar,
Expression.Call(AnotherFunctionCallMethodInfo, returnVar)));
}
if (boolB)
{
commands.Add(
Expression.Assign(
returnVar,
Expression.Call(ThirdFunctionCallMethodInfo, returnVar)));
}
commands.Add(returnVar);
var body = Expression.Block(new[] { returnVar }, commands);
return Expression.Lambda<Func<object, object>>(body, objectParameter).Compile();
}
Call this function from your code Func<object, object> processor = CreateFunc(boolA,boolB);
I have the following class
public class MyClass
{
public bool Delete(Product product)
{
// some code.
}
}
Now I have a helper class that looks like this
public class Helper<T, TResult>
{
public Type Type;
public string Method;
public Type[] ArgTypes;
public object[] ArgValues;
public Helper(Expression<Func<T, TResult>> expression)
{
var body = (System.Linq.Expressions.MethodCallExpression)expression.Body;
this.Type = typeof(T);
this.Method = body.Method.Name;
this.ArgTypes = body.Arguments.Select(x => x.Type).ToArray();
this.ArgValues = ???
}
}
The idea ist to use this code from somewhere:
// I am returning a helper somewhere
public Helper<T> GetMethod<T>()
{
var product = GetProduct(1);
return new Helper<MyClass>(x => x.Delete(product));
}
// some other class decides, when to execute the helper
// Invoker already exists and is responsible for executing the method
// that is the main reason I don't just comile and execute my Expression
public bool ExecuteMethod<T>(Helper<T> helper)
{
var instance = new MyClass();
var Invoker = new Invoker(helper.Type, helper.Method, helper.ArgTypes, helper.ArgValues);
return (bool)Invoker.Invoke(instance);
}
The point where I am stuck is how to extract the arguments from the expression itself.
I found this way
((ConstantExpression)((MemberExpression)body.Arguments[0]).Expression).Value
which seems to be an object type with a field "product" but I believe there must be a simpler solution.
Any suggestions.
Update
Just to clarify, I modified my code according to what I want to achive. In my real word application I already have a class that does the same but without an expression tree:
var helper = new Helper(typeof(MyClass), "Delete",
new Type[] { typeof(Product) }, new object[] {product}));
The main reason for my Helper<T> is to have Compile-Time checking if the method signature is valid.
Update 2
This is my current implementation, is there a better way to acces the values, without using reflection?
public Helper(Expression<Func<T, TResult>> expression)
{
var body = (System.Linq.Expressions.MethodCallExpression)expression.Body;
this.Type = typeof(T);
this.Method = body.Method.Name;
this.ArgTypes = body.Arguments.Select(x => x.Type).ToArray();
var values = new List<object>();
foreach(var arg in body.Arguments)
{
values.Add(
(((ConstantExpression)exp.Expression).Value).GetType()
.GetField(exp.Member.Name)
.GetValue(((ConstantExpression)exp.Expression).Value);
);
}
this.ArgValues = values.ToArray();
}
This method works pretty well. It returns the argument types and values for an Expression>
private static KeyValuePair<Type, object>[] ResolveArgs<T>(Expression<Func<T, object>> expression)
{
var body = (System.Linq.Expressions.MethodCallExpression)expression.Body;
var values = new List<KeyValuePair<Type, object>>();
foreach (var argument in body.Arguments)
{
var exp = ResolveMemberExpression(argument);
var type = argument.Type;
var value = GetValue(exp);
values.Add(new KeyValuePair<Type, object>(type, value));
}
return values.ToArray();
}
public static MemberExpression ResolveMemberExpression(Expression expression)
{
if (expression is MemberExpression)
{
return (MemberExpression)expression;
}
else if (expression is UnaryExpression)
{
// if casting is involved, Expression is not x => x.FieldName but x => Convert(x.Fieldname)
return (MemberExpression)((UnaryExpression)expression).Operand;
}
else
{
throw new NotSupportedException(expression.ToString());
}
}
private static object GetValue(MemberExpression exp)
{
// expression is ConstantExpression or FieldExpression
if (exp.Expression is ConstantExpression)
{
return (((ConstantExpression)exp.Expression).Value)
.GetType()
.GetField(exp.Member.Name)
.GetValue(((ConstantExpression)exp.Expression).Value);
}
else if (exp.Expression is MemberExpression)
{
return GetValue((MemberExpression)exp.Expression);
}
else
{
throw new NotImplementedException();
}
}
You can compile the argument expression and then invoke it to calculate the value:
var values = new List<object>();
foreach(var arg in body.Arguments)
{
var value = Expression.Lambda(argument).Compile().DynamicInvoke();
values.Add(value);
}
this.ArgValues = values.ToArray();
Here is an example of creation of a delegate using a lambda. The object instance is encapsulated into the delegate using a C# feature called closure.
MyClass instance = new MyClass();
//This following line cannot be changed to var declaration
//since C# can't infer the type.
Func<Product, bool> deleteDelegate = p => instance.Delete(p);
Product product = new Product();
bool deleted = deleteDelegate(product);
Alternatively you are trying to create a Helper that automagically Currys.
public class Helper<T>
where T : new()
{
public TResult Execute<TResult>(Func<T, TResult> methodLambda)
{
var instance = new T();
return methodLamda(instance);
}
}
public void Main()
{
var helper = new Helper<MyClass>();
var product = new Product();
helper.Execute(x => x.Delete(product));
}
However I have to say this problem looks suspiciously like the creation of a Helper class to handle the lifetime of a WCF proxy....You know...just say...in which case this ISN'T how I would approach this...simply because this approach leaks WCF specific code into your domain.
Let's say I have the following class.
MyClass<T>
{
public void MyMethod(T a, List<T> b, List<Tuple<T, string>> c) {}
}
I can get the type of the arguments of the method as follow
Type testType = typeof(MyClass<>);
MethodInfo myMethodInfo = testType.GetMethod("MyMethod");
Type[] paramTypes = myMethodInfo.GetParameters().Select(pi => pi.ParameterType);
How can I manually create an array containing the same open types as paramTypes from a string? For ex from
var typesAsStr = new string[] {"T", "List`1[T]", "List`1[Tuple`2[T, string]]"};
If I had MyClass<int>, I could do something like Type.GetType(fullQualifiedNameOfArg) for each argument, but here I want to keep the generic argument T:
I can't create "a": I can't do Type.GetType("T")
I can almost create "b": I can do Type.GetType("List `1"), but the info on "T" is not yet present
I don't know how to create "c"
I ended up needing this when converting a Mono.Cecil type into a .net type: Cecil gives me the info on a method named "MyMethod" with arguments "T", "List<T>" and "List<Tuple<T, string>>". I then want to get that method using reflection (if there are several methods with the same name and argument numbers, I have to check the args to know which one it is), that's why I'd want to have a way to transform what Cecil tells me into what .Net knows, to be able to compare with what's in paramTypes.
I've also seen several other people asking how to convert a Mono.Cecil type into a .Net one, so that's also why I thought I'd try.
You can get T using strings, you do it by calling GetType with the string name of MyClass and then getting the generic arguments of the resulting type. From there you can build up the other open generic types using MakeGenericType. You have to work from the inside out by constructing the most nested types first. To do it automatically across differing methods would require some string parsing to get to the nested types. For the sake of comparing .Net methods against Cecil methods, #Tengiz might have a better approach.
To run the code, update the string name of MyClass to have the correct namespace for your environment.
private static void Main(string[] args) {
// change 'yournamespace'
Type testType = Type.GetType("yournamespace.MyClass`1");
Type[] testTypeGenericArgs = testType.GetGenericArguments();
// Get T type from MyClass generic args
Type tType = testTypeGenericArgs[0];
Type genericListType = Type.GetType("System.Collections.Generic.List`1");
// create type List<T>
Type openListType = genericListType.MakeGenericType(testTypeGenericArgs[0]);
Type genericTuple = Type.GetType("System.Tuple`2");
Type stringType = Type.GetType("System.String");
// create type Tuple<T, string>
Type openTuple = genericTuple.MakeGenericType(new[] { tType, stringType });
// create type List<Tuple<T, string>>
Type openListOfTuple = genericListType.MakeGenericType(openTuple);
Type[] typesFromStrings = new[] { tType, openListType, openListOfTuple };
// get method parameters per example
Type myClassType = typeof(MyClass<>);
MethodInfo myMethodInfo = myClassType.GetMethod("MyMethod");
Type[] paramTypes = myMethodInfo.GetParameters().Select(pi => pi.ParameterType).ToArray();
// compare type created from strings against types
// retrieved by reflection
for (int i = 0; i < typesFromStrings.Length; i++) {
Console.WriteLine(typesFromStrings[i].Equals(paramTypes[i]));
}
Console.ReadLine();
}
I found this so interesting, that I had to create something myself, and present it to the world... and after a couple hours of exploration, here is what I got...
The extension method for Type: GetMethodByString
This is very simple: get a type and then call the method passing a string that represents the method you want:
var type = typeof(MyType<>);
type.GetMethodByString("MyMethod(T, List`1[T], List`1[Tuple`2[T, String]])")
Sample program
class Program
{
public static void Main()
{
var t1 = typeof(MyType<>);
var mi11 = t1.GetMethodByString("MyMethod(T, List`1[T], List`1[Tuple`2[T, String]])");
var mi12 = t1.GetMethodByString("Method[X](X, T)");
var mi13 = t1.GetMethodByString("Method(List`1[T], Int32 ByRef)");
var t2 = typeof(MyType);
var mi21 = t2.GetMethodByString("Method[X, T](List`1[X], Tuple`2[X, List`1[T]])");
}
class MyType<T>
{
public void MyMethod(T a, List<T> b, List<Tuple<T, string>> c) { }
public void Method(List<T> t, out int i) { i = 0; }
public void Method<X>(X x, T t) { }
}
class MyType
{
public int Method<X, T>(List<X> x, Tuple<X, List<T>> tuple)
{
return 1;
}
}
}
TypeExtensions
public static class TypeExtensions
{
public static MethodInfo GetMethodByString(
this Type type, string methodString)
{
return type.GetMethods()
.Where(mi => MethodToString(mi) == methodString)
.SingleOrDefault();
}
public static string MethodToString(MethodInfo mi)
{
var b = new StringBuilder();
b.Append(mi.Name);
if (mi.IsGenericMethodDefinition)
b.AppendFormat("[{0}]",
string.Join(", ", mi.GetGenericArguments()
.Select(TypeToString)));
b.AppendFormat("({0})", string.Join(", ", mi.GetParameters()
.Select(ParamToString)));
return b.ToString();
}
public static string TypeToString(Type t)
{
var b = new StringBuilder();
b.AppendFormat("{0}", t.Name);
if (t.IsGenericType)
b.AppendFormat("[{0}]",
string.Join(", ", t.GetGenericArguments()
.Select(TypeToString)));
return b.ToString();
}
public static string ParamToString(ParameterInfo pi)
{
return TypeToString(pi.ParameterType).Replace("&", " ByRef");
}
}
Why I didn't try to get types by name
Unfortunately, I found no way to get a type given a string, unless you guess a lot about the type being represented... so, it is quite impossible.
That explains why I did a method to find the method instead. It is much more precise... but it could eventually fail, in very rare and bizarre circumstances:
if you create a List of your own, and then two overloads of the same method, one taking the .Net List and the other taking the List you have created... then it fails
Why not parsing the input string
I found that for the purpose of looking up a method, it is enough to have a fixed syntax string, so that I can generate it from the method and compare... that have some limitations:
must use the name of the type, so C# alliases won't work (string must be named "String", int must be named "Int32" not "int")
EDIT
Performance
This solution is not very performatic, but nothing that a cache cannot solve. The method could use a dictionary, using both the Type and the string as a composite key, and look in there before trying to find the method by bulding a lot of strings and comparing all of them.
If you need thread safety on the cache dictionary, use a ConcurrentDictionary<TKey, TValue>... very nice class.
EDIT 2: Created a cached version
static ConcurrentDictionary<Type, Dictionary<string, MethodInfo>> cacheOfGetMethodByString
= new ConcurrentDictionary<Type, Dictionary<string, MethodInfo>>();
public static MethodInfo GetMethodByString(
this Type type, string methodString)
{
var typeData = cacheOfGetMethodByString
.GetOrAdd(type, CreateTypeData);
MethodInfo mi;
typeData.TryGetValue(methodString, out mi);
return mi;
}
public static Dictionary<string, MethodInfo> CreateTypeData(Type type)
{
var dic = new Dictionary<string, MethodInfo>();
foreach (var eachMi in type.GetMethods())
dic.Add(MethodToString(eachMi), eachMi);
return dic;
}
Hoppe this helps! =)
I don't think .NET allows you to create a type "T" where T is a type argument, which is yet to be specified. So, the array of Type(s) from input string array cannot be created.
However, in the second part of your question, I read that you want to identify the method which has those types given as string. That task is solvable by iterating though the arguments, creating another array of strings describing the method arguments, and then comparing the resulting and input arrays, as follows:
class MyClass<T>
{
public void MyMethod(T a, List<T> b, List<Tuple<T, string>> c) { }
}
class Program
{
static void Main(string[] args)
{
//input.
var typesAsStr = new string[] { "T", "List`1[T]", "List`1[Tuple`2[T, string]]" };
//type to find a method.
Type testType = typeof(MyClass<>);
//possibly iterate through methods instead?
MethodInfo myMethodInfo = testType.GetMethod("MyMethod");
//get array of strings describing MyMethod's arguments.
string[] paramTypes = myMethodInfo.GetParameters().Select(pi => TypeToString(pi.ParameterType)).ToArray();
//compare arrays of strings (can be improved).
var index = -1;
Console.WriteLine("Method found: {0}", typesAsStr.All(str => { index++; return index < paramTypes.Length && str == paramTypes[index]; }));
Console.ReadLine();
}
private static CSharpCodeProvider compiler = new CSharpCodeProvider();
private static string TypeToString(Type type)
{
if (type.IsGenericType) {
return type.Name + "[" + string.Join(", ", type.GetGenericArguments().Select(ga => TypeToString(ga))) + "]";
}
else if (type.IsGenericParameter) {
return type.Name;
}
//next line gives "string" (lower case for System.String).
//additional type name translations can be applied if output is not what we neeed.
return compiler.GetTypeOutput(new CodeTypeReference(type));
}
}
In the [console] output I see that your input string matches the function.
BTW, a lot of optimizations can be applied to this code if you face the performance problems, such as efficient way of working with strings, releasing CSharpCodeProvider instance maybe, etc. But the code is enough to solve the given task as questioned.
You cannot do what you are trying to do, but there is a relatively easy way of achieving the same result by entering from a different direction
Strings do not identify types uniquely
This is the basic problem with converting strings to types: when you see a T, you have no idea where it came from. The following is a valid class definition:
class Simple<T> {
public T Make(T blah) {
return blah;
}
public T Make<T>(T blah) {
return blah;
}
}
Two overloads of Make have parameters that look identical, yet they do not compare as equal. Moreover, there is absolutely no way of getting the T of the generic Make<T> without first getting the MethodInfo for the generic Make<T> - a circular dependency.
What can you do?
Instead of going for the impossible string->Type conversion, you can build a matcher that tells you if an instance of a type, including an unbounded generic type, matches a given string representation:
static bool MatchType(string str, Type type)
With this method in hand, you can walk through all available methods with a particular name, and check the types of their parameter lists one by one against the strings in your array of strings:
var typesAsStr = new [] {"T", "List`1[T]", "List`1[Tuple`2[T, string]]"};
var myMethod = typeof (Simple<>)
.GetMethods()
.SingleOrDefault(m => m.Name == "MyMethod" &&
typesAsStr
.Zip(m.GetParameters(), (s, t) => new {s, t})
.All(p => MatchType(p.s, p.t.ParameterType))
);
How do you implement the MatchType method?
You can use a technique similar to Recursive Descent Parsing: tokenize your string, and then match elements of your type as you go through the chain of tokens. When a class is parameterized, get generic parameters and match them recursively. You need to pay attention to array types, but that is relatively simple as well. Take a look:
public static bool MatchType(string str, Type type) {
var queue = new Queue<Token>(Tokenize(str));
return MatchRecursive(queue, type) && (queue.Count == 0);
}
private static bool MatchRecursive(Queue<Token> tokens, Type type) {
string baseName;
if (!ReadToken(tokens, TokenType.Identifier, out baseName)) return false;
var ranks = new List<int>();
while (type.IsArray) {
ranks.Add(type.GetArrayRank());
type = type.GetElementType();
}
if (type.IsGenericType) {
if (!type.Name.StartsWith(baseName+"`") || !DropToken(tokens, TokenType.Tick)) return false;
string numStr;
int num;
if (!ReadToken(tokens, TokenType.Number, out numStr)
|| !int.TryParse(numStr, out num)
|| !DropToken(tokens, TokenType.OpenBraket)) return false;
var genParams = type.GetGenericArguments();
if (genParams.Length != num) return false;
for (var i = 0 ; i < num ; i++) {
if (i != 0 && !DropToken(tokens, TokenType.Comma)) return false;
if (!MatchRecursive(tokens, genParams[i])) return false;
}
if (!DropToken(tokens, TokenType.CloseBraket)) return false;
}
foreach (var rank in ranks) {
if (!DropToken(tokens, TokenType.OpenBraket)) return false;
for (var i = 0 ; i != rank-1 ; i++) {
if (!DropToken(tokens, TokenType.Comma)) return false;
}
if (!DropToken(tokens, TokenType.CloseBraket)) return false;
}
return type.IsGenericType || Aliases.Contains(new Tuple<string, Type>(baseName, type)) || type.Name == baseName;
}
private static readonly ISet<Tuple<string,Type>> Aliases = new HashSet<Tuple<string, Type>> {
new Tuple<string, Type>("bool", typeof(bool)),
new Tuple<string, Type>("byte", typeof(byte)),
new Tuple<string, Type>("sbyte", typeof(sbyte)),
new Tuple<string, Type>("char", typeof(char)),
new Tuple<string, Type>("string", typeof(string)),
new Tuple<string, Type>("short", typeof(short)),
new Tuple<string, Type>("ushort", typeof(ushort)),
new Tuple<string, Type>("int", typeof(int)),
new Tuple<string, Type>("uint", typeof(uint)),
new Tuple<string, Type>("long", typeof(long)),
new Tuple<string, Type>("ulong", typeof(ulong)),
new Tuple<string, Type>("float", typeof(float)),
new Tuple<string, Type>("double", typeof(double)),
new Tuple<string, Type>("decimal", typeof(decimal)),
new Tuple<string, Type>("void", typeof(void)),
new Tuple<string, Type>("object", typeof(object))
};
private enum TokenType {
OpenBraket,
CloseBraket,
Comma,
Tick,
Identifier,
Number
}
private class Token {
public TokenType Type { get; private set; }
public string Text { get; private set; }
public Token(TokenType type, string text) {
Type = type;
Text = text;
}
public override string ToString() {
return string.Format("{0}:{1}", Enum.GetName(typeof(TokenType), Type), Text);
}
}
private static bool DropToken(Queue<Token> tokens, TokenType expected) {
return (tokens.Count != 0) && (tokens.Dequeue().Type == expected);
}
private static bool ReadToken(Queue<Token> tokens, TokenType expected, out string text) {
var res = (tokens.Count != 0) && (tokens.Peek().Type == expected);
text = res ? tokens.Dequeue().Text : null;
return res;
}
private static IEnumerable<Token> Tokenize(IEnumerable<char> str) {
var res = new List<Token>();
var text = new StringBuilder();
foreach (var c in str) {
var pos = "[],`".IndexOf(c);
if ((pos != -1 || char.IsWhiteSpace(c)) && text.Length != 0) {
res.Add(new Token(
char.IsDigit(text[0]) ? TokenType.Number : TokenType.Identifier
, text.ToString())
);
text.Clear();
}
if (pos != -1) {
res.Add(new Token((TokenType)pos, c.ToString(CultureInfo.InvariantCulture)));
} else if (!char.IsWhiteSpace(c)) {
text.Append(c);
}
}
if (text.Length != 0) {
res.Add(new Token(
char.IsDigit(text[0]) ? TokenType.Number : TokenType.Identifier
, text.ToString())
);
}
return res;
}
It is not quite clear to me what the exactly you need, but i believe you can use the following technique:
object[] parameters = CreateParameters(typeof(MyClass<>), "MyMethod", typeof(int));
Debug.Assert(parameters[0] is int);
Debug.Assert(parameters[1] is List<int>);
Debug.Assert(parameters[2] is List<Tuple<int, string>>);
//...
object[] CreateParameters(Type type, string methodName, Type genericArgument) {
object[] parameters = null;
MethodInfo mInfo = type.GetMethod(methodName);
if(mInfo != null) {
var pInfos = mInfo.GetParameters();
parameters = new object[pInfos.Length];
for(int i = 0; i < pInfos.Length; i++) {
Type pType = pInfos[i].ParameterType;
if(pType.IsGenericParameter)
parameters[i] = Activator.CreateInstance(genericArgument);
if(pType.IsGenericType) {
var arguments = ResolveGenericArguments(pType, genericArgument);
Type definition = pType.GetGenericTypeDefinition();
Type actualizedType = definition.MakeGenericType(arguments);
parameters[i] = Activator.CreateInstance(actualizedType);
}
}
}
return parameters;
}
Type[] ResolveGenericArguments(Type genericType, Type genericArgument) {
Type[] arguments = genericType.GetGenericArguments();
for(int i = 0; i < arguments.Length; i++) {
if(arguments[i].IsGenericParameter)
arguments[i] = genericArgument;
if(arguments[i].IsGenericType) {
var nestedArguments = ResolveGenericArguments(arguments[i], genericArgument);
Type nestedDefinition = arguments[i].GetGenericTypeDefinition();
arguments[i] = nestedDefinition.MakeGenericType(nestedArguments);
}
}
return arguments;
}
I have an third party library that has the following API:
Update<TReport>(object updateOnly, Expression<Func<TReport,bool>> where)
What I want to do is call this method but using anonymous objects such as :
Update(new {Name = "test"}, new {Id = id})
Is it possible to take the second anonymous object and convert it to something like :
x => x.Id == id.
So what I want is to convert the new {Id = id} to a function that takes TReport and returns bool?
Even if I agree with Daniel A. White on the fact that it's complicating things, I tried a bit.
But it's not safe, cause you're losing strong typing. (You can put whatever you want in an anonymous object : it's not linked to the object's "real" properties... So no refactoring, no check...)
It's not really tested, so not sure if that's what you want. You can have (if it works) different objects in the "predicate object" :
new {Name="test"}, new{Id=1, Name="test2"})
So, you could have something like that :
public static class MyHelpers
{
public static Expression<Func<TReport, bool>> CreatePredicate<TReport>(this object predicateObject)
{
var parameterExpression = Expression.Parameter(typeof(TReport), "item");
Expression memberExpression = parameterExpression;
var objectDictionary = MakeDictionary(predicateObject);
foreach (var entry in objectDictionary.Where(entry => typeof(TReport).GetProperty(entry.Key) == null))
{
throw new ArgumentException(string.Format("Type {0} has no property {1}", typeof(TReport).Name, entry.Key));
}
var equalityExpressions = GetBinaryExpressions(objectDictionary, memberExpression).ToList();
var body = equalityExpressions.First();
body = equalityExpressions.Skip(1).Aggregate(body, Expression.And);
return Expression.Lambda<Func<TReport, bool>>(body, new[] { parameterExpression });
}
private static IDictionary<string, object> MakeDictionary(object withProperties)
{
var properties = TypeDescriptor.GetProperties(withProperties);
return properties.Cast<PropertyDescriptor>().ToDictionary(property => property.Name, property => property.GetValue(withProperties));
}
private static IEnumerable<BinaryExpression> GetBinaryExpressions(IDictionary<string, object> dic, Expression expression)
{
return dic.Select(m => Expression.Equal(Expression.Property(expression, m.Key), Expression.Constant(m.Value)));
}
}
usage, for example
public void Update<TReport>(object updateOnly, object predicateObject) {
var predicate = predicateObject.CreatePredicate<TReport>();
yourGenericApi.Update(updateOnly, predicate);
}
EDIT :
As you're losing strong typing security, you should add something like
foreach (var entry in objectDictionary.Where(entry => typeof(TReport).GetProperty(entry.Key) == null))
{
throw new ArgumentException(string.Format("Type {0} has no property {1}", typeof(TReport).Name, entry.Key));
}
after
var objectDictionary = MakeDictionary(predicateObject);
If you have a specific value that you want the function to return, I think you can simply do this:
bool desiredResult = true;
Update(new { Name = "test" }, x => desiredResult);