Create open constructed type from string - c#

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

Related

Dynamic parameter list of different out types in C#

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.

How to get arguments from an Expression where TDelegate is a callback

I'm attempting to write a simple generic cache but running into problems with generating unique enough keys with using System.Func as a callback.
What I ideally want is to be able to pass in an invocable delegate of some description so that the cache itself can get the value, and determine a key all from the same expression. Right now I'm getting exceptions because I'm not passing in an argument that implements or inherits from MethodCallExpression. What should I be using instead of a System.Func for this intended behaviour?
public class SimpleCacheKeyGenerator : ICacheKey
{
public string GetCacheKey<T>(Expression<Func<T>> action)
{
var body = (MethodCallExpression) action.Body; //!!! Exception Raised - action.Body is FieldExpression
ICollection<object> parameters = (from MemberExpression expression in body.Arguments
select
((FieldInfo) expression.Member).GetValue(
((ConstantExpression) expression.Expression).Value)).ToList();
var sb = new StringBuilder(100);
sb.Append(body.Type.Namespace);
sb.Append("-");
sb.Append(body.Method.Name);
parameters.ToList().ForEach(x =>
{
sb.Append("-");
sb.Append(x);
});
return sb.ToString();
}
}
public class InMemoryCache : ICacheService
{
private readonly ICachePolicy _cachePolicy;
private readonly ICacheKey _cacheKey;
public InMemoryCache(ICachePolicy cachePolicy, ICacheKey cacheKey)
{
_cachePolicy = cachePolicy;
_cacheKey = cacheKey;
}
public T Get<T>(Func<T> getItemCallback) where T : class
{
var cacheID = _cacheKey.GetCacheKey(() => getItemCallback);
var item = HttpRuntime.Cache.Get(cacheID) as T;
if (item == null)
{
item = getItemCallback();
if (_cachePolicy.RenewLeaseOnAccess)
{
HttpContext.Current.Cache.Insert(cacheID, getItemCallback, null, System.Web.Caching.Cache.NoAbsoluteExpiration, _cachePolicy.ExpiresAfter);
}
else
{
HttpContext.Current.Cache.Insert(cacheID, getItemCallback, null, DateTime.UtcNow + _cachePolicy.ExpiresAfter, System.Web.Caching.Cache.NoSlidingExpiration);
}
}
return item;
}
}
The problem is, you can't easily use both the Expression> and Func representing the same thing without duplicating the code.
You could possibly convert Expression> to a Func with LambdaExpression>.Compile() method, but that could create a performance problem, since Compile actually uses assembly emit, which is quite expensive.
Here is how i would implement the same thing without using Expressions and compilation.
You can find the same pattern everywhere in the standard Linq extensions.
Pass your argument as a separate object.
The type you use as an argument will be used for type inference for the delegate, and the argument itself will provide the arguments for the delegate at the same type.
Note that the cache in this implementation works because of the default ToString implementation of the anonimous objects used as arguments.
void Main()
{
var computeCount = 0;
var item1 = GetCached(new{x = 1, y = 2}, (arg)=>{computeCount++; return arg.x + arg.y;});
Console.WriteLine(item1);
var item2 = GetCached(new{x = 1, y = 2}, (arg)=>{computeCount++; return arg.x + arg.y;});
Console.WriteLine(item2);
var item3 = GetCached(new{x = 1, y = 3}, (arg)=>{computeCount++; return arg.x + arg.y;});
Console.WriteLine(item3);
Console.WriteLine("Compute count:");
Console.WriteLine(computeCount);
}
Dictionary<string, object> _cache = new Dictionary<string, object>();
E GetCached<T, E>(T arg, Func<T,E> getter)
{
// Creating the cache key.
// Assuming T implements ToString correctly for cache to work.
var cacheKey = arg.ToString();
object result;
if (!_cache.TryGetValue(cacheKey, out result))
{
var newItem = getter(arg);
_cache.Add(cacheKey, newItem);
return newItem;
}
else
{
Console.WriteLine("Cache hit: {0}", cacheKey);
}
return (E)result;
}
Console output:
3
Cache hit: { x = 1, y = 2 }
3
4
Compute count:
2
You get this exception because (() => getItemCallback) means (() => { return getItemCallback; })
That's why action.Body is not a method call, it is the return statement. If you change your code to (() => getItemCallback()) you should not have the error. But you won't have any arguments.
To obtain arguments of the base call, you will have to change your code to accept an Expression and Compile your lambda.
public T Get<T>(Expression<Func<T>> getItemCallbackExpression) where T : class
{
var cacheID = _cacheKey.GetCacheKey(getItemCallbackExpression);
var item = HttpRuntime.Cache.Get(cacheID) as T;
if (item == null)
{
item = getItemCallback.Compile()();
if (_cachePolicy.RenewLeaseOnAccess)
{
HttpContext.Current.Cache.Insert(cacheID, getItemCallback, null, System.Web.Caching.Cache.NoAbsoluteExpiration, _cachePolicy.ExpiresAfter);
}
else
{
HttpContext.Current.Cache.Insert(cacheID, getItemCallback, null, DateTime.UtcNow + _cachePolicy.ExpiresAfter, System.Web.Caching.Cache.NoSlidingExpiration);
}
}
return item;
}
I won't recommend this approach because compiling an expression takes time.
It may be easier and more performant to manually generate cache keys. If you really want to automatically manage cache keys. You may have a look to Aspect Oriented Programmation using castle.Core or PostSharp. Theses tools will allow you to automatically add code to some of your methods and automatically add cache logic.
I modified the code as below, I got the expected result this way, so you can try this, I hope this would be helpful.
public class SimpleCacheKeyGenerator
{
public string GetCacheKey<T, TObject>(Expression<Func<T, TObject>> action)
{
var body = (MethodCallExpression) action.Body;
ICollection<object> parameters = body.Arguments.Select(x => ((ConstantExpression) x).Value).ToList();
var sb = new StringBuilder(100);
sb.Append(body.Type.Namespace);
sb.Append("-");
sb.Append(body.Method.Name);
parameters.ToList().ForEach(x =>
{
sb.Append("-");
sb.Append(x);
});
return sb.ToString();
}
}
public class InMemoryCache
{
public void Get<T, TObject>(Expression<Func<T, TObject>> getItemCallback)
{
var generator = new SimpleCacheKeyGenerator();
Console.WriteLine(generator.GetCacheKey(getItemCallback));
}
}
main:
private static void Main(string[] args)
{
var cache = new InMemoryCache();
var tt = new SomeContextImpl();
cache.Get<SomeContextImpl, string>(x => x.Any("hello", "hi"));
Console.ReadKey();
}
somcontextimpl:
public class SomeContextImpl
{
public string Any(string parameter1, string parameter2) { return ""; }
}

Manually infering variable types from a list of examples in string format

I have a List<string> of sample data points that I have read from the file. I am looking for a way to infer the most appropriate type for the examples that are in the list. For example imagine the list is initialized like the following:
var samples = new List<string>()
{
"1",
"2",
"2.01"
};
I am looking for a method that accepts this list and returns System.double. I am wondering if there is any internal C# method that I can use. my data can be either int, double or a string so I can always try to cast them into int and if it fails cast it into double and if that fails for all element return System.string. Is there any easier way of doing it?
Methods are supposed to have a single return type, there are ways around this but it would be better to return a string and just have wherever you're calling from handle the string how you want for example.
String s=method(samples);
int i;
try
{
i=s.ToDecimal();
}
catch (FormatException e)
{
Console.WriteLine("Input string is not a sequence of digits.");
}
catch (OverflowException e)
{
Console.WriteLine("The number cannot fit in an Int32.");
}
As far as I know there's nothing built-in, but you could write your own:
public delegate bool TryParseDelegate<T>(string str, out T value);
public static Tuple<Func<string, bool>, Type> ParsesAs<T>(TryParseDelegate<T> d)
{
Func<string, bool> f = s =>
{
T val;
return d(s, out val);
};
return Tuple.Create(f, typeof(T));
}
public static Type FindBestType(List<string> input)
{
var parsers = new List<Tuple<Func<string, bool>, Type>>
{
ParsesAs<int>(int.TryParse),
ParsesAs<double>(double.TryParse)
};
int i = 0;
foreach (string str in input)
{
while (i < parsers.Count && !parsers[i].Item1(str))
{
i++;
}
if (i == parsers.Count) break;
}
return i == parsers.Count ? typeof(string) : parsers[i].Item2;
}
Assuming it's only int/double/string you can do something like this. It's not optimized by any means. Just something straightforward that may get you on the right track.
public static Type InferType(List<string> samples)
{
Type stringType= typeof(string);
Type intType = typeof(int);
Type doubleType = typeof(double);
List<Type> types = new List<Type>();
for (int i = 0; i < samples.Count; i++)
{
int intRepresentation;
double doubleRepresentation;
if (int.TryParse(samples[i], out intRepresentation))
{
types.Add(intType);
}
else if (double.TryParse(samples[i], out doubleRepresentation))
{
types.Add(doubleType);
}
else
{
types.Add(stringType);
break;
}
}
return types.Any(e => e == stringType) ? stringType :
types.Any(e => e == doubleType) ? doubleType :
intType;
}

How to unbox from object to type it contains, not knowing that type at compile time?

At the run-time I get boxed instance of some type. How to unbox it to underlying type?
Object obj;
String variable = "Some text";
obj = variable // boxing;
// explicit unboxing, because we know the type of variable at compile time.
var x = (String)obj
// Now let's pretend that we don't know the type of underlying object at compile time.
Type desiredType = obj.GetType(); // But we can figure out.
//And now the question.
//How to express something like this:
var y = (desiredType)obj; //Need to get unboxed instance of initial variable here;
If you don't know the type at compile time, then you can't unbox because you have nowhere to put it - all you can do is store it in an object, which is: boxed.
The same also applies to reference-types like string: you can't cast it to the right type if you don't know the type at compile time: you have nowhere to put it.
You can special-case a few types, for example:
if(obj is int) {
int i = (int)obj;
...
} ...
Another trick that is sometimes (not often) helpful is to switch into generics; then instead of talking in terms of object you are talking in terms of T. This has... limited use though. The easiest way to do that is via dynamic, for example:
dynamic obj = ...
Foo(obj);
...
Foo<T>(T val) { ... code with T ... }
you can also add special cases to that appreach:
Foo(string val) { ... code with string ...}
Foo(int val) { ... code with int ...}
However, frankly I suggest it may be better to look hard at what you are trying to do.
Now lets suppose, that real boxing occur:
int v = 5;
object o = v; //boxed
Type type = o.GetType(); //will return typeof(int)
int convertedBack = (int)Convert.ChangeType(o, type);
Console.WriteLine (convertedBack); //prints 5
Remark, if you substitute:
object convertedBack = Convert.ChangeType(o, type);
Console.WriteLine (convertedBack); //it still prints 5
Console.WriteLine (o); //it even print 5 here
The reason is that underlying object is still int. I've just used this example to show you, that boxing is irrelevant here. You need to rely on some abstraction in your operations and if you want to cast to int dynamically, what reference type do you wan to use.
In such cases I'm going to use the strategy pattern by using a Dictionary<Type, Action<object>>:
internal class Program
{
private static void Main(string[] args)
{
var something = new Something();
something.ComputeValue(13);
something.ComputeValue(DateTime.Now);
something.ComputeValue(DayOfWeek.Monday);
Console.ReadKey();
}
}
internal class Something
{
private static Dictionary<Type, Action<object>> _Strategies;
static Something()
{
// Prepare all available strategies.
_Strategies = new Dictionary<Type, Action<object>>();
_Strategies.Add(typeof(int), ComputeInteger);
_Strategies.Add(typeof(DateTime), ComputeDateTime);
}
public void ComputeValue(object value)
{
Action<object> action;
// Check if we have a matching strategy.
if (!_Strategies.TryGetValue(value.GetType(), out action))
{
// If not, log error, throw exception, whatever.
action = LogUnknownType;
}
// Perform the matching strategy on the given value.
action(value);
}
private static void ComputeDateTime(object source)
{
// We get an object, but we are sure that it will always be an DateTime.
var value = (DateTime)source;
Console.WriteLine("We've got an date time: " + value);
}
private static void ComputeInteger(object source)
{
// We get an object, but we are sure that it will always be an int.
var value = (int)source;
Console.WriteLine("We've got an integer: " + value);
}
private static void LogUnknownType(object source)
{
// What should we do with the drunken sailor?
var unknownType = source.GetType();
Console.WriteLine("Don't know how to handle " + unknownType.FullName);
}
}
Here's an example of exactly why you would do this:
class MyClass
{
public int Id {get;set;}
public string Name {get;set;}
public decimal Val {get;set;}
}
int i = 0;
var myClassImp = new MyClass();
foreach (var val in new [object]{"10", "My name", "100.21"} // Could be read from some data source, such as an excel spreadsheet
{
var prop = typeof(MyClass).GetProperties().ElementAt(i++);
// !!!!!! THROWS EXCEPTION !!!!!!!
prop.SetValue(myClassImp, System.Convert.ChangeType(val, prop.PropertyType), null);
}
The reason for this is because the value is a boxed object... at runtime you do not know the type, so you would have to unbox to the prop.PropertyType
A pragmatic solution; try and use a TypeConverter directly, and if that fails, convert to string and back again:-
private static T GetValueOfType<T>(this ManagementBaseObject MBO, String FieldName) {
T lResult;
try {
Object lObj = MBO[FieldName];
var lSrcType = lObj.GetType();
var lDestType = typeof(T);
if (lDestType.IsValueType && lDestType.IsAssignableFrom(lSrcType)) {
lResult = (T)lObj;
return lResult;
}
var lDestTC = TypeDescriptor.GetConverter(typeof(T));
if (lDestTC.CanConvertFrom(lSrcType)) {
lResult = (T)lDestTC.ConvertFrom(lObj);
} else {
var lSrcTC = TypeDescriptor.GetConverter(lSrcType);
String lTmp = lSrcTC.ConvertToInvariantString(lObj);
lResult = (T)lDestTC.ConvertFromInvariantString(lTmp);
}
} catch {
lResult = default(T);
}
return lResult;
}
Use expressions:
var y = DynamicCast(obj, desiredType);
static object DynamicCast(object source, Type type)
{
var parameter = Expression.Parameter(typeof(object), "input");
var cast = Expression.TypeAs(Expression.Convert(parameter, type), typeof(object));
var lambda = Expression.Lambda<Func<object, object>>(cast, parameter);
var func = lambda.Compile();
return func(source);
}
public static string GetType(object data)
{
Type type = data.GetType();
return Convert.ChangeType(data, type).GetType().Name;
}
Hi,this method receives object data and returns string type name of object.
Hope this is what you need.
you can try using the dynamic runtime
[Test]
public void Test_UnboxingAtRuntime()
{
object boxed = "Hello";
//this line is commented out as it does not compile
// OverloadedMethod(boxed);
var result = CallCorrectMethod(boxed);
Assert.That(result, Is.EqualTo("string"));
boxed = 1;
result = CallCorrectMethod(boxed);
Assert.That(result, Is.EqualTo("int"));
}
public string CallCorrectMethod(dynamic t)
{
return OverloadedMethod(t);
}
public string OverloadedMethod(string s)
{
return "string";
}
public string OverloadedMethod(int s)
{
return "int";
}

How to cast a generic type at runtime in c#

I need to create an IEnumerable<IEnumerable<T>> when I only know T at runtime.
I have built up my collection like so:
new List<List<object>>()
where all the objects in the inner list are a T
However because of co/contravariance (can never remember which it is!) my List of Lists isnt an IEnumerable of IEnumerables.
What can I do about this?
I've tried using Convert.ChangeType but it moans that List isn't IConvertible
Clue: Read the question. Again. I said I only know T at runtime.
OK, based on Master Morality's answer, I've come up with this. Shockingly simple.
public static IEnumerable Cast(this IEnumerable self, Type innerType)
{
var methodInfo = typeof (Enumerable).GetMethod("Cast");
var genericMethod = methodInfo.MakeGenericMethod(innerType);
return genericMethod.Invoke(null, new [] {self}) as IEnumerable;
}
Simple. Blogged about it here: Casting an enumerable when the inner type is only known at runtime
I've had similar issues with TinyIoC, and rather than "converting", the "cleanest" solution I've found is to make your method generic (so public IEnumerable'T DoStuff'T()), then call that using MakeGenericMethod using your runtime type. It stays "clean" because your actual method that constructs the list just operates as if it is a normal generic method, so it doesn't get cluttered with casting etc.
Without seeing your code it's hard to know if that fits the bill - here's the relevant bits to make the generic method from TinyIoc:
public static class TypeExtensions
{
private static SafeDictionary<GenericMethodCacheKey, MethodInfo> _genericMethodCache;
static TypeExtensions()
{
_genericMethodCache = new SafeDictionary<GenericMethodCacheKey, MethodInfo>();
}
/// <summary>
/// Gets a generic method from a type given the method name, binding flags, generic types and parameter types
/// </summary>
/// <param name="sourceType">Source type</param>
/// <param name="bindingFlags">Binding flags</param>
/// <param name="methodName">Name of the method</param>
/// <param name="genericTypes">Generic types to use to make the method generic</param>
/// <param name="parameterTypes">Method parameters</param>
/// <returns>MethodInfo or null if no matches found</returns>
/// <exception cref="System.Reflection.AmbiguousMatchException"/>
/// <exception cref="System.ArgumentException"/>
public static MethodInfo GetGenericMethod(this Type sourceType, System.Reflection.BindingFlags bindingFlags, string methodName, Type[] genericTypes, Type[] parameterTypes)
{
MethodInfo method;
var cacheKey = new GenericMethodCacheKey(sourceType, methodName, genericTypes, parameterTypes);
// Shouldn't need any additional locking
// we don't care if we do the method info generation
// more than once before it gets cached.
if (!_genericMethodCache.TryGetValue(cacheKey, out method))
{
method = GetMethod(sourceType, bindingFlags, methodName, genericTypes, parameterTypes);
_genericMethodCache[cacheKey] = method;
}
return method;
}
private static MethodInfo GetMethod(Type sourceType, BindingFlags bindingFlags, string methodName, Type[] genericTypes, Type[] parameterTypes)
{
var methods =
sourceType.GetMethods(bindingFlags).Where(
mi => string.Equals(methodName, mi.Name, StringComparison.InvariantCulture)).Where(
mi => mi.ContainsGenericParameters).Where(mi => mi.GetGenericArguments().Length == genericTypes.Length).
Where(mi => mi.GetParameters().Length == parameterTypes.Length).Select(
mi => mi.MakeGenericMethod(genericTypes)).Where(
mi => mi.GetParameters().Select(pi => pi.ParameterType).SequenceEqual(parameterTypes)).ToList();
if (methods.Count > 1)
{
throw new AmbiguousMatchException();
}
return methods.FirstOrDefault();
}
private sealed class GenericMethodCacheKey
{
private readonly Type _sourceType;
private readonly string _methodName;
private readonly Type[] _genericTypes;
private readonly Type[] _parameterTypes;
private readonly int _hashCode;
public GenericMethodCacheKey(Type sourceType, string methodName, Type[] genericTypes, Type[] parameterTypes)
{
_sourceType = sourceType;
_methodName = methodName;
_genericTypes = genericTypes;
_parameterTypes = parameterTypes;
_hashCode = GenerateHashCode();
}
public override bool Equals(object obj)
{
var cacheKey = obj as GenericMethodCacheKey;
if (cacheKey == null)
return false;
if (_sourceType != cacheKey._sourceType)
return false;
if (!String.Equals(_methodName, cacheKey._methodName, StringComparison.InvariantCulture))
return false;
if (_genericTypes.Length != cacheKey._genericTypes.Length)
return false;
if (_parameterTypes.Length != cacheKey._parameterTypes.Length)
return false;
for (int i = 0; i < _genericTypes.Length; ++i)
{
if (_genericTypes[i] != cacheKey._genericTypes[i])
return false;
}
for (int i = 0; i < _parameterTypes.Length; ++i)
{
if (_parameterTypes[i] != cacheKey._parameterTypes[i])
return false;
}
return true;
}
public override int GetHashCode()
{
return _hashCode;
}
private int GenerateHashCode()
{
unchecked
{
var result = _sourceType.GetHashCode();
result = (result * 397) ^ _methodName.GetHashCode();
for (int i = 0; i < _genericTypes.Length; ++i)
{
result = (result * 397) ^ _genericTypes[i].GetHashCode();
}
for (int i = 0; i < _parameterTypes.Length; ++i)
{
result = (result * 397) ^ _parameterTypes[i].GetHashCode();
}
return result;
}
}
}
}
Which is called as follows:
private object GetIEnumerableRequest(Type type)
{
var genericResolveAllMethod = this.GetType().GetGenericMethod(BindingFlags.Public | BindingFlags.Instance, "ResolveAll", type.GetGenericArguments(), new[] { typeof(bool) });
return genericResolveAllMethod.Invoke(this, new object[] { false });
}
And ResolveAll is defined as:
public IEnumerable<ResolveType> ResolveAll<ResolveType>()
where ResolveType : class
{
return ResolveAll<ResolveType>(true);
}
Hope that makes sense :)
Use it untyped as IEnumerable<IEnumerable>
Use reflection to call a function that takes a IEnumerable<IEnumerable<T>> with the appropriate T
use a switch statement to cast to appropriate type
use dynamic
Examples
static IEnumerable<IEnumerable<T>> castList<T>(List<List<object>> list) {
return list.Select(x => x.Cast<T>());
}
void DoSomething(Type myT, List<List<object>> list) {
object untyped = typeof(MyClass).GetMethod("castList")
.MakeGenericMethod(myT)
.Invoke(null, new[] { list });
// untyped is an IEnumerable<IEnumerable<myT>> at runtime,
// but obviously you don't know that at compile time.
// what can you do with untyped?
// 1: use it like an untyped container
var option1 = (IEnumerable<IEnumerable>)untyped;
foreach(var inner in option1)
foreach(object item in inner)
Console.WriteLine(object);
// 2: pass it to a function that you reflect on using
// the above makeGenericMethod strategy
typeof(MyClass).GetMethod("Process")
.MakeGenericMethod(myT)
.Invoke(null, new[] { untyped });
// 3: Cast it conditionally
switch(Type.GetTypeCode(myT)) {
case TypeCode.Int32:
Process((IEnumerable<IEnumerable<int>>)untyped);
break;
case TypeCode.Single:
Process((IEnumerable<IEnumerable<float>>)untyped);
break;
}
// 4: make it a dynamic
dynamic dyn = untyped;
Process(dyn);
}
static void Process<T>(IEnumerable<IEnumerable<T>> ienumerable) {
Console.WriteLine("Processing type: {0}", typeof(T).Name);
foreach(var inner in ienumerable)
foreach(T item in inner)
DoSomething(item); // item is now type T
}
Edit: If you only know T at run time, you could do it by building an expression. and compiling it. like so:
var listOfLists = new List<List<object>>();
//... do list building...
//types
var runTimeType = typeof(MyRuntimeType);
var innerListType = typeof(List<>)
.MakeGenericType(typeof(object));
var innerEnumerableType = typeof(IEnumerable<>)
.MakeGenericType(runTimeType);
var outerListType = typeof(List<>)
.MakeGenericType(innerListType);
//methods
var castm = typeof(Enumerable).GetMethod("Cast")
.MakeGenericMethod(runTimeType);
var selectm = typeof(Enumerable).GetMethods()
.Where(x => x.Name == "Select").First()
.MakeGenericMethod(innerListType, innerEnumerableType);
//expressions (parameters)
var innerParamx = Expression.Parameter(innerListType);
var outerParamx = Expression.Parameter(outerListType);
// listOfLists.Select(x => x.Cast<T>());
// as an expression
var castx = Expression.Call(castm, innerParamx);
var lambdax = Expression.Lambda(castx, innerParamx);
var selectx = Expression.Call(selectm, outerParamx, lambdax);
var lambdax2 = Expression.Lambda(selectx, outerParamx);
var result = lambdax2.Compile().DynamicInvoke(listOfLists);
you could optionally cached lambdax2.Compile() somewhere for each runtime type, performance.
I believe the answer is "you can't" - though I could probably be proven wrong with some super hacky code that uses a ton of reflection or emits IL directly or something.
The compiler and JIT'er needs to know the object types for everything to setup the stack, allocate memory properly, etc.
Perhaps every type T can implement some marker interface, or derive from a common base? Various behaviors can be implemented virtually. If you can comment a bit on what your program is trying to do, perhaps people could come up with a good design.
Based on your comment,
not exactly but thanks, i could create the inner list of the right
type, but then i can push objects into it and i still have the
variance issue
what I gather is that you get a variance issue on the objects that you are adding to the outer list although you are able to cast the inner list.
Based on this link, what I understand is that you can use a workaround to instantiate the outer list,
// Simple workaround for single method
// Variance in one direction only
public static void Add<S, D>(List<S> source, List<D> destination)
where S : D
{
foreach (S sourceElement in source)
{
destination.Add(sourceElement);
}
}
public IEnumerable<IEnumerable<T>> void Test<T>()
{
// Create a top IEnumeranble instance you should specify list element type
var result = new List<IEnumerable<T>>();
// Add an internal IEnumerable<T>
result.Add(new List<T>());
return result;
}
but if you already has an initialized List<List<T>> you just need a cast:
list.Cast<IEnumerable<T>>();

Categories

Resources