I'm struggling with implementing the IEquatable<> interface for a class. The class has a Parameter property that uses a generic type. Basically the class definition is like this:
public class MyClass<T> : IEquatable<MyClass<T>>
{
public T Parameter { get; }
...
}
In the Equals() method I'm using EqualityComparer<T>.Default.Equals(Parameter, other.Parameter) to compare the property. Generally, this works fine – as long as the property is not a collection, for example an IEnumerable<T>. The problem is that the default equality comparer for IEnumerable<T> is checking reference equality.
Obviously, you'd want to use SequenceEqual() to compare the IEnumerable<T>. But to get this running, you need to specify the generic type of the SequenceEqual() method. This is the closest I could get:
var parameterType = typeof(T);
var enumerableType = parameterType.GetInterfaces()
.Where(type => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
.Select(type => type.GetGenericArguments().First()).FirstOrDefault();
if (enumerableType != null)
{
var castedThis = Convert.ChangeType(Parameter, enumerableType);
var castedOther = Convert.ChangeType(other.Parameter, enumerableType);
var isEqual = castedThis.SequenceEqual(castedOther);
}
But this does not work because Convert.ChangeType() returns an object. And of course object does not implement SequenceEqual().
How do I get this working? Thanks for any tipps!
Best regards,
Oliver
Given that you have a generic container that you want to compare various generic items, you don't want to be hard coding in various specific equality checks for certain types. There are going to be lots of situations where the default equality comparison won't work for what some particular caller is trying to do. The comments have numerous different examples of problems that can come up, but also just consider the many many classes out there who's default equality is a reference comparison by for which someone might want a value comparison. You can't have this equality comparer just hard code in a solution for all of those types.
The solution of course is easy. Let the caller provide their own equality implementation, which in C#, means an IEqualityComparer<T>. Your class can become:
public class MyClass<T> : IEquatable<MyClass<T>>
{
private IEqualityComparer<T> comparer;
public MyClass(IEqualityComparer<T> innerComparer = null)
{
comparer = innerComparer ?? EqualityComparer<T>.Default;
}
public T Parameter { get; }
...
}
And now by default the default comparer will be used for any given type, but the caller can always specify a non-default comparer for any type that needs different equality semantics.
Effectively you want a way to say
var castedThis = (IEnumerable<U>)Convert.ChangeType(Parameter, enumerableType);
where T is IEnumerable<U> and U is dynamic.
I don't think you can do that.
If you are happy with some boxing though, you can use the non-generic IEnumerable interface:
public bool Equals(MyClass<T> other)
{
var parameterType = typeof(T);
if (typeof(IEnumerable).IsAssignableFrom(parameterType))
{
var castedThis = ((IEnumerable)this.Parameter).GetEnumerator();
var castedOther = ((IEnumerable)other.Parameter).GetEnumerator();
try
{
while (castedThis.MoveNext())
{
if (!castedOther.MoveNext())
return false;
if (!Convert.Equals(castedThis.Current, castedOther.Current))
return false;
}
return !castedOther.MoveNext();
}
finally
{
(castedThis as IDisposable)?.Dispose();
(castedOther as IDisposable)?.Dispose();
}
}
else
{
return EqualityComparer<T>.Default.Equals(this.Parameter, other.Parameter);
}
}
If you are not happy with the boxing, then you can use reflection to construct and call SequenceEqual (as inspired by How do I invoke an extension method using reflection?):
public bool Equals(MyClass<T> other)
{
var parameterType = typeof(T);
if (typeof(IEnumerable).IsAssignableFrom(parameterType))
{
var enumerableType = parameterType.GetGenericArguments().First();
var sequenceEqualMethod = typeof(Enumerable)
.GetMethods(BindingFlags.Static | BindingFlags.Public)
.Where(mi => {
if (mi.Name != "SequenceEqual")
return false;
if (mi.GetGenericArguments().Length != 1)
return false;
var pars = mi.GetParameters();
if (pars.Length != 2)
return false;
return pars[0].ParameterType.IsGenericType && pars[0].ParameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>) && pars[1].ParameterType.IsGenericType && pars[1].ParameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>);
})
.First()
.MakeGenericMethod(enumerableType)
;
return (bool)sequenceEqualMethod.Invoke(this.Parameter, new object[] { this.Parameter, other.Parameter });
}
else
{
return EqualityComparer<T>.Default.Equals(this.Parameter, other.Parameter);
}
}
You can cache the sequenceEqualMethod for better performance.
I'm trying to call a generic method at run time, and have working code. However, I was wondering if there was a better way of getting the method info, as should i change the method name, it will break.
public int Handle<T>(CreateCodeModel obj) where T : CodeModel
{
//Create code here...
}
//codeType is selected by user.
public int Handle(CreateCodeModel obj, Type codeType)
{
MethodInfo method = this.GetType().GetMethod("Handle");
//MethodInfo method = this.GetType().GetMethods()
.Where(mi => mi.IsGenericMethod && mi.Name == "Handle").First();
MethodInfo genericMethod = method.MakeGenericMethod(new Type[] { codeType });
return (int)genericMethod.Invoke(this, new object[] { obj });
}
I had hoped there was a nicer way of doing this, perhaps using actions to get the method info, but then I still need to provide a type, e.g.
Action<CreateCodeModel> h = (x) => Handle(x);
MethodInfo method = h.Method;
You are on the right track here. Your delegate signature must match the method you are calling, so in this case that would be Func<CreateCodeModel,int> instead of Action<CreateCodeModel>. And you must supply a generic parameter that matches the generic constraints. This could be anything since we'll just remove it with our call to 'GetGenericMethodDefinition', but I like to use the class from the constraint.
The other piece of the code below is the idea that you only need to get the generic method definition once, as it doesn't change, so I store that in a static variable so it can be reused.
public int Handle<T>(CreateCodeModel obj) where T : CodeModel
{
//Create code here...
return 7;
}
// this static variable preserves the generic MethodInfo, so we don't have
// keep discovering it with reflection
private static MethodInfo _methodInfoForHandle;
// The generic MethodInfo only needs to be discovered the first time this runs
private MethodInfo MethodInfoForHandle
{
get
{
return _methodInfoForHandle ?? (_methodInfoForHandle = GetMethodInfoForHandleMethod());
}
}
private MethodInfo GetMethodInfoForHandleMethod()
{
Func<CreateCodeModel, int> handleFunc = Handle<CodeModel>;
return handleFunc.Method.GetGenericMethodDefinition();
}
//codeType is selected by user.
public int Handle(CreateCodeModel obj, Type codeType)
{
MethodInfo genericMethod = MethodInfoForHandle.MakeGenericMethod(new Type[] { codeType });
return (int)genericMethod.Invoke(this, new object[] { obj });
}
public class CreateCodeModel { }
public class CodeModel { }
public class JavascriptCodeModel : CodeModel { }
The following throws an InvalidCastException.
IEnumerable<int> list = new List<int>() { 1 };
IEnumerable<long> castedList = list.Cast<long>();
Console.WriteLine(castedList.First());
Why?
I'm using Visual Studio 2008 SP1.
That's very odd! There's a blog post here that describes how the behaviour of Cast<T>() was changed between .NET 3.5 and .NET 3.5 SP1, but it still doesn't explain the InvalidCastException, which you even get if you rewrite your code thus:
var list = new[] { 1 };
var castedList = from long l in list select l;
Console.WriteLine(castedList.First());
Obviously you can work around it by doing the cast yourself
var castedList = list.Select(i => (long)i);
This works, but it doesn't explain the error in the first place. I tried casting the list to short and float and those threw the same exception.
Edit
That blog post does explain why it doesn't work!
Cast<T>() is an extension method on IEnumerable rather than IEnumerable<T>. That means that by the time each value gets to the point where it's being cast, it has already been boxed back into a System.Object. In essence it's trying to do this:
int i = 1;
object o = i;
long l = (long)o;
This code throws the InvalidCastException you're getting. If you try to cast an int directly to a long you're fine, but casting a boxed int back to a long doesn't work.
Certainly an oddity!
Enumerable.Cast method is defined as following:
public static IEnumerable<TResult> Cast<TResult>(
this IEnumerable source
)
And there is no information about initial type of IEnumerable's items, so I think each of your ints is initially converted to System.Object via boxing and then it's tried to be unboxed into long variable and this is incorrect.
Similar code to reproduce this:
int i = 1;
object o = i; // boxing
long l = (long)o; // unboxing, incorrect
// long l = (int)o; // this will work
So solution for your problem will be:
ints.Select(i => (long)i)
I'm at it again!
Here's the solution to all your List<T> and Enumerable<T> conversion problems.
~150 lines of code
Just be sure to define at least one explicit or implicit conversion operator for the input/output types involved (if one does not exist), as you should be doing anyway!
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace System.Collections.Generic //purposely in same namespace as List<T>,IEnumerable<T>, so extension methods are available with them
{
public static class Enumerable
{
public static List<TOutput> ConvertAll<TInput,TOutput>( this IEnumerable<TInput> input ) {
return BuildConvertedList<TInput,TOutput>( input, GetConverterDelegate<TInput,TOutput>() );
}
public static IEnumerable<TOutput> ConvertAll<TInput,TOutput>( this IEnumerable<TInput> input, bool lazy ) {
if (lazy) return new LazyConverter<TInput,TOutput>( input, GetConverterDelegate<TInput,TOutput>() );
return BuildConvertedList<TInput,TOutput>( input, GetConverterDelegate<TInput,TOutput>() );
}
public static List<TOutput> ConvertAll<TInput,TOutput>( this IEnumerable<TInput> input, Converter<TInput, TOutput> converter ) {
return BuildConvertedList<TInput,TOutput>( input, converter );
}
public static List<TOutput> ConvertAll<TInput, TOutput>( this List<TInput> input ) {
Converter<TInput, TOutput> converter = GetConverterDelegate<TInput,TOutput>();
return input.ConvertAll<TOutput>( converter );
}
public static IEnumerable<TOutput> ConvertAll<TInput, TOutput>( this List<TInput> input, Converter<TInput, TOutput> converter, bool lazy ) {
if (lazy) return new LazyConverter<TInput, TOutput>( input, converter );
return input.ConvertAll<TOutput>( converter );
}
public static List<TOutput> ConvertAll<TInput, TOutput>( this List<TInput> input, Converter<TInput, TOutput> converter ) {
return input.ConvertAll<TOutput>( converter );
}
//Used to manually build converted list when input is IEnumerable, since it doesn't have the ConvertAll method like the List does
private static List<TOutput> BuildConvertedList<TInput,TOutput>( IEnumerable<TInput> input, Converter<TInput, TOutput> converter ){
List<TOutput> output = new List<TOutput>();
foreach (TInput input_item in input)
output.Add( converter( input_item ) );
return output;
}
private sealed class LazyConverter<TInput, TOutput>: IEnumerable<TOutput>, IEnumerator<TOutput>
{
private readonly IEnumerable<TInput> input;
private readonly Converter<TInput, TOutput> converter;
private readonly IEnumerator<TInput> input_enumerator;
public LazyConverter( IEnumerable<TInput> input, Converter<TInput, TOutput> converter )
{
this.input = input;
this.converter = converter;
this.input_enumerator = input.GetEnumerator();
}
public IEnumerator<TOutput> GetEnumerator() {return this;} //IEnumerable<TOutput> Member
IEnumerator IEnumerable.GetEnumerator() {return this;} //IEnumerable Member
public void Dispose() {input_enumerator.Dispose();} //IDisposable Member
public TOutput Current {get {return converter.Invoke( input_enumerator.Current );}} //IEnumerator<TOutput> Member
object IEnumerator.Current {get {return Current;}} //IEnumerator Member
public bool MoveNext() {return input_enumerator.MoveNext();} //IEnumerator Member
public void Reset() {input_enumerator.Reset();} //IEnumerator Member
}
private sealed class TypeConversionPair: IEquatable<TypeConversionPair>
{
public readonly Type source_type;
public readonly Type target_type;
private readonly int hashcode;
public TypeConversionPair( Type source_type, Type target_type ) {
this.source_type = source_type;
this.target_type = target_type;
//precalc/store hash, since object is immutable; add one to source hash so reversing the source and target still produces unique hash
hashcode = (source_type.GetHashCode() + 1) ^ target_type.GetHashCode();
}
public static bool operator ==( TypeConversionPair x, TypeConversionPair y ) {
if ((object)x != null) return x.Equals( y );
if ((object)y != null) return y.Equals( x );
return true; //x and y are both null, cast to object above ensures reference equality comparison
}
public static bool operator !=( TypeConversionPair x, TypeConversionPair y ) {
if ((object)x != null) return !x.Equals( y );
if ((object)y != null) return !y.Equals( x );
return false; //x and y are both null, cast to object above ensures reference equality comparison
}
//TypeConversionPairs are equal when their source and target types are equal
public bool Equals( TypeConversionPair other ) {
if ((object)other == null) return false; //cast to object ensures reference equality comparison
return source_type == other.source_type && target_type == other.target_type;
}
public override bool Equals( object obj ) {
TypeConversionPair other = obj as TypeConversionPair;
if ((object)other != null) return Equals( other ); //call IEqualityComparer<TypeConversionPair> implementation if obj type is TypeConversionPair
return false; //obj is null or is not of type TypeConversionPair; Equals shall not throw errors!
}
public override int GetHashCode() {return hashcode;} //assigned in constructor; object is immutable
}
private static readonly Dictionary<TypeConversionPair,Delegate> conversion_op_cache = new Dictionary<TypeConversionPair,Delegate>();
//Uses reflection to find and create a Converter<TInput, TOutput> delegate for the given types.
//Once a delegate is obtained, it is cached, so further requests for the delegate do not use reflection*
//(*the typeof operator is used twice to look up the type pairs in the cache)
public static Converter<TInput, TOutput> GetConverterDelegate<TInput, TOutput>()
{
Delegate converter;
TypeConversionPair type_pair = new TypeConversionPair( typeof(TInput), typeof(TOutput) );
//Attempt to quickly find a cached conversion delegate.
lock (conversion_op_cache) //synchronize with concurrent calls to Add
if (conversion_op_cache.TryGetValue( type_pair, out converter ))
return (Converter<TInput, TOutput>)converter;
//Get potential conversion operators (target-type methods are ordered first)
MethodInfo[][] conversion_op_sets = new MethodInfo[2][] {
type_pair.target_type.GetMethods( BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy ),
type_pair.source_type.GetMethods( BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy )
};
//Find appropriate conversion operator,
//favoring operators on target type in case functionally equivalent operators exist,
//since the target type's conversion operator may have access to an appropriate constructor
//or a common instance cache (i.e. immutable objects may be cached and reused).
for (int s = 0; s < conversion_op_sets.Length; s++) {
MethodInfo[] conversion_ops = conversion_op_sets[s];
for (int m = 0; m < conversion_ops.Length; m++)
{
MethodInfo mi = conversion_ops[m];
if ((mi.Name == "op_Explicit" || mi.Name == "op_Implicit") &&
mi.ReturnType == type_pair.target_type &&
mi.GetParameters()[0].ParameterType.IsAssignableFrom( type_pair.source_type )) //Assuming op_Explicit and op_Implicit always have exactly one parameter.
{
converter = Delegate.CreateDelegate( typeof(Converter<TInput, TOutput>), mi );
lock (conversion_op_cache) //synchronize with concurrent calls to TryGetValue
conversion_op_cache.Add( type_pair, converter ); //Cache the conversion operator reference for future use.
return (Converter<TInput, TOutput>)converter;
}
}
}
return (TInput x) => ((TOutput)Convert.ChangeType( x, typeof(TOutput) )); //this works well in the absence of conversion operators for types that implement IConvertible
//throw new InvalidCastException( "Could not find conversion operator to convert " + type_pair.source_type.FullName + " to " + type_pair.target_type.FullName + "." );
}
}
}
Sample use:
using System;
using System.Collections.Generic;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
List<string> list = new List<string>(new string[] { "abcde", "abcd", "abc"/*will break length constraint*/, "ab", "a" });
//Uncomment line below to see non-lazy behavior. All items converted before method returns, and will fail on third item, which breaks the length constraint.
//List<ConstrainedString> constrained_list = list.ConvertAll<string,ConstrainedString>();
IEnumerable<ConstrainedString> constrained_list = list.ConvertAll<string,ConstrainedString>( true ); //lazy conversion; conversion is not attempted until that item is read
foreach (ConstrainedString constrained_string in constrained_list) //will not fail until the third list item is read/converted
System.Console.WriteLine( constrained_string.ToString() );
}
public class ConstrainedString
{
private readonly string value;
public ConstrainedString( string value ){this.value = Constrain(value);}
public string Constrain( string value ) {
if (value.Length > 3) return value;
throw new ArgumentException("String length must be > 3!");
}
public static explicit operator ConstrainedString( string value ){return new ConstrainedString( value );}
public override string ToString() {return value;}
}
}
}
Hmm... interesting puzzle. All the more interesting given that I just ran it in Visual Studio 2008 and it didn't throw at all.
I'm not using Service Pack 1, and you might be, so that could be the issue. I know there were some "performance enhancements" in .Cast() in the SP1 release that could be causing the issue. Some reading:
Blog Entry 1
Blog Entry 2
I wish they could have done something intelligent, like using any implicit or explicit cast operators defined for the type. The current behavior and inconsistency is unacceptable. Absolutely useless in its current condition.
After realizing that Cast<Type> was throwing an exception instead of using the cast operators that I defined for the type, I became irritated and found this thread. If it's defined for IEnumerable, why wouldn't they just implement it to use reflection to get the type of object, get the target type, discover any available static conversion operators, and find an appropriate one to do the cast. It could cast a heterogeneous IEnumerable into an IEnumerable<T>.
The following implementation is a working idea...
public static class EnumerableMinusWTF
{
public static IEnumerable<TResult> Cast<TResult,TSource>(this IEnumerable<TSource> source)
{
Type source_type = typeof(TSource);
Type target_type = typeof(TResult);
List<MethodInfo> methods = new List<MethodInfo>();
methods.AddRange( target_type.GetMethods( BindingFlags.Static | BindingFlags.Public ) ); //target methods will be favored in the search
methods.AddRange( source_type.GetMethods( BindingFlags.Static | BindingFlags.Public ) );
MethodInfo op_Explicit = FindExplicitConverstion(source_type, target_type, methods );
List<TResult> results = new List<TResult>();
foreach (TSource source_item in source)
results.Add((TResult)op_Explicit.Invoke(null, new object[] { source_item }));
return results;
}
public static MethodInfo FindExplicitConverstion(Type source_type, Type target_type, List<MethodInfo> methods)
{
foreach (MethodInfo mi in methods)
{
if (mi.Name == "op_Explicit") //will return target and take one parameter
if (mi.ReturnType == target_type)
if (mi.GetParameters()[0].ParameterType == source_type)
return mi;
}
throw new InvalidCastException( "Could not find conversion operator to convert " + source_type.FullName + " to " + target_type.FullName + "." );
}
}
I can then call this code successfully:
//LessonID inherits RegexConstrainedString, and has explicit conversion operator defined to convert string to LessonID
List<string> lessons = new List<String>(new string[] {"l001,l002"});
IEnumerable<LessonID> constrained_lessons = lessons.Cast<LessonID, string>();
Here are some things to think about...
Do you want to cast or convert?
Do you want the result as a List<T> or an IEnumerable<T>.
If the result is an IEnumerable<T>, do you want the cast/convert to be applied lazily (i.e. the cast/convert will not actually occur until the iterator reaches each element)?
Useful distinction between cast/convert, since a casting operator often involves constructing a new object, and could be considered a conversion:
"Cast" implementations should automatically apply conversion operators defined for the types involved; a new object may or may not be constructed.
"Convert" implementations should allow one to specify a System.Converter<TInput,TOutput> delegate.
Potential method headers:
List<TOutput> Cast<TInput,TOutput>(IEnumerable<TInput> input);
List<TOutput> Convert<TInput,TOutput>(IEnumerable<TInput> input, Converter<TInput,TOutput> converter);
IEnumerable<TOutput> Cast<TInput,TOutput>(IEnumerable<TInput> input);
IEnumerable<TOutput> Convert<TInput,TOutput>(IEnumerable<TInput> input, Converter<TInput,TOutput> converter);
Problematic "Cast" implementations using existing framework; suppose you pass as input a List<string>, that you want to convert with any of the previous methods.
//Select can return only a lazy read-only iterator; also fails to use existing explicit cast operator, because such a cast isn't possible in c# for a generic type parameter (so says VS2008)
list.Select<TInput,TOutput>( (TInput x) => (TOutput)x );
//Cast fails, unless TOutput has an explicit conversion operator defined for 'object' to 'TOutput'; this confusion is what lead to this topic in the first place
list.Cast<TOuput>();
Problematic "Convert" implementations
//Again, the cast to a generic type parameter not possible in c#; also, this requires a List<T> as input instead of just an IEnumerable<T>.
list.ConvertAll<TOutput>( new Converter<TInput,TOuput>( (TInput x) => (TOutput)x ) );
//This would be nice, except reflection is used, and must be used since c# hides the method name for explicit operators "op_Explicit", making it difficult to obtain a delegate any other way.
list.ConvertAll<TOutput>(
(Converter<TInput,TOutput>)Delegate.CreateDelegate(
typeof(Converter<TInput,TOutput>),
typeof(TOutput).GetMethod( "op_Explicit", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public )
)
);
Summary:
Cast/Convert methods should involve defined explicit conversion operators, or allow one to specify a conversion delegate. C#'s language specification for conversion operators -- specifically their lack of a method name -- makes it difficult to obtain a delegate except through reflection. The alternative is to encapsulate or replicate the conversion code, increasing the (maintenance) complexity of your code unnecessarily, since really, possible/allowed conversions are implicit in the presence or absence of conversion operators, and should be handled by the compiler. We should not have to manually search for cryptically-named definitions (e.g. "op_Explicit") of appropriate conversion operators with reflection at RUN TIME on the types involved. Furthermore, Cast/Convert methods for bulk/list conversions using explicit conversion operators should really be a framework feature, and with List.ConvertAll<T>, they are... except the language spec makes it hard to get a delegate for the conversion operators efficiently!!!
Of course, the sane thing to do is to use Select(i => (long)i) and that's what I would recommend for conversions between built-in value types and for user-defined conversion.
But just as a curious remark, since .NET 4 it is possible to make your own extension method that also works with these kinds of conversions. But it requires that you're willing to use the dynamic keyword. It goes simply like this:
public static IEnumerable<TResult> CastSuper<TResult>(this IEnumerable source)
{
foreach (var s in source)
yield return (TResult)(dynamic)s;
}
As I said, works with integral conversions (narrowing or widening conversions), numerical conversions to/from/between floating-point types, and conversion "methods" of the kinds implicit operator and explicit operator.
And of course it still works with the good old reference conversions and unboxing conversions like the original System.Enumerable.Cast<TResult>.
Consider the following types in an assembly: BusinessPartnerList, BusinessPartner, PrivateData, CompanyData, AddressList, Address
Type BusinessPartnerList
{
BusinessPartner[]
}
Type BusinessPartner
{
PrivateData
CompanyData
AddressList
}
Type PrivateData
{
System.String FirstName
System.String SurName
}
Type PrivateData
{
System.String CompanName1
System.String CompanName2
}
Type AddressList
{
Address[]
}
I want to generic parse the type hierarchy, and represent them in a tree e.g. simple nodes
BusinessPartnerList[]
BusinessPartner
PrivateData
CompanyData
AddressList[]
Address
What is the best way to do this?
Unfortunately you didn't use proper C# syntax for your sample data. So I have to make some assumptions:
Type is actually class (or struct).
The contents of the types (BusinessPartner, PrivateData, CompanyData etc.) represent the types of some public properties.
To parse the type hierarchy you can use reflection. Find all public properties of a given type and return their types. Since you only want the types you can use a HashSet which will only contain distinct types:
public static HashSet<Type> GetPropertyTypes(Type type)
{
return new HashSet<Type>(type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Select(prop => prop.PropertyType));
}
However, it seems that you don't want to get information on arrays but rather on the type of the array elements. The same goes for lists. So if a type implements IEnumerable<T> you want to get information on the type T:
private static Type GetElementType(Type type)
{
Type enumerableType = type.GetInterfaces().FirstOrDefault(IsGenericEnumerable);
if (enumerableType != null)
{
Type[] genericArguments = enumerableType.GetGenericArguments();
return genericArguments[0];
}
// return 'object' for a non-generic IEnumerable
return typeof(IEnumerable).IsAssignableFrom(type) ? typeof(object) : type;
}
private static bool IsGenericEnumerable(Type type)
{
return type.IsGenericType &&
type.GetGenericTypeDefinition() == typeof(IEnumerable<>);
}
Note that for the type System.String this will return char because string implements IEnumerable<char> (I will adress that later).
The .NET framework does not have a tree structure you can use out of the box. So you need to implement it yourself:
public class Node<T>
{
public Node(T value, IEnumerable<Node<T>> children)
{
Value = value;
Children = children.ToList();
}
public T Value
{
get;
private set;
}
public List<Node<T>> Children
{
get;
private set;
}
}
This is a very basic implementation just for demonstration purposes.
Instead of returning List<Type> the GetPropertyTypes method can now return Node<Type> and it should be renamed to CreateTypeNode:
public static Node<Type> CreateTypeNode(Type type)
{
var children = type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Select(prop => GetElementType(prop.PropertyType))
.Select(CreateTypeNode);
return new Node<Type>(type, children);
}
This method uses recursion to create the full tree for the given type.
There is still a problem: What if type A references type B and vice versa? This would end up in an infinite recursive loop. And also: if a type has already been visited there is no need to do that again.
What we need is a cache for the types that have been visited. If a type is in the cache we use the information from the cache:
private static readonly Dictionary<Type, Node<Type>> _visitedTypes = new Dictionary<Type, Node<Type>>();
public static Node<Type> CreateTypeNode(Type type)
{
Node<Type> node;
if (_visitedTypes.TryGetValue(type, out node))
{
return node;
}
// add the key to the cache to prevent infinite recursion; the value will be set later
// if this type will be found again in a recursive call CreateTypeNode returns null
// (null will be filtered out then)
_visitedTypes.Add(type, null);
var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
var types = new HashSet<Type>(properties.Select(prop => GetElementType(prop.PropertyType)));
var children = types.Select(CreateTypeNode).Where(n => n != null);
node = new Node<Type>(type, children);
_visitedTypes[type] = node;
return node;
}
I you don't want the string type to be reported as char (because string implements IEnumerable<char>) you can just add a node for string to the cache before you call GetOrCreateTypeNode for the first time:
_visitedTypes.Add(typeof(string), new Node<Type>(typeof(string), new List<Node<Type>>()));
Then check the cache in the GetElementType method:
private static Type GetElementType(Type type)
{
if (_visitedTypes.ContainsKey(type))
{
return type;
}
...
}
It's there a way to invoke a method and the return type is strong typed ?
There is an example of code
public static IQueryable<T> FilterVersion<T>(this Table<T> t, IVersionIndexFilter version)
where T : class
{
try
{
// Define the new type of my table
Type versionTableType = Type.GetType(typeof(T).AssemblyQualifiedName.Replace(typeof(T).FullName, string.Concat(typeof(T).FullName, VersionIndexTableExtensionName)));
// Get the method info generic with a custom helper
var getTableType = MethodInfoHelper.GetGenericMethod(typeof(DataContext), "GetTable", new Type[] { versionTableType }, new Type[] { }, typeof(Table<>).MakeGenericType(typeof(Table<>).GetGenericArguments()[0]), BindingFlags.Public | BindingFlags.Instance);
// Get the object with a invoke but invoke return object, i need he return a Table<vesrionTableType>
var joinTable = getTableType.Invoke(t.Context, null);
// Put my new object in anonymous class
var result = new { FromT = t, InnerT = joinTable };
// Write the type of the property and is {System.Object InnerT}
Console.Write(result.GetType().GetProperty("InnerT"));
}
I need my Console.Write(result.GetType().GetProperty("InnerT")); return a Table<versionTableType>
It's there a way i cand do that ? Any suggestion ?
There is my GetGenericMethod
public static MethodInfo GetGenericMethod(Type t, string name, Type[] genericArgTypes, Type[] argTypes, Type returnType, BindingFlags flags)
{
if (genericArgTypes == null)
{
genericArgTypes = new Type[] { };
}
MethodInfo genericMethod = (from m in t.GetMethods(flags)
where m.Name == name
&& m.GetGenericArguments().Length == genericArgTypes.Length
&& m.GetParameters().Select(pi => pi.ParameterType.IsGenericType ? pi.ParameterType.GetGenericTypeDefinition() : pi.ParameterType).SequenceEqual(argTypes) &&
(returnType == null || (m.ReturnType.IsGenericType ? m.ReturnType.GetGenericTypeDefinition() : m.ReturnType) == returnType)
select m).FirstOrDefault();
if (genericMethod != null)
{
return genericMethod.MakeGenericMethod(genericArgTypes);
}
return null;
}
I get my method info generic correctly. The probleme is when i assign to another property the result of my invoke is an object. I need to strong type my result
EDIT : It's a good idea the static method CreateResult but not enough ... there is what i try to do after with my value
NewExpression exResult = Expression.New(result.GetType().GetConstructor(new Type[] { t.GetType(), typeof(Table<>).MakeGenericType(versionTableType) }), new List<Expression>() { outer, inner }, result.GetType().GetProperty("FromT"), result.GetType().GetProperty("InnerT"));
There is the error i got after try the technique
Argument type 'System.Data.Linq.Table`1[Nms.Media.Business.Manager.Data.Linq.ImageLibraryItemBinaryLocaleVersionIndex]' does not match the corresponding member type 'System.Object'
Thanks
EDIT: Okay, since your question edit it looks like you're already calling MakeGenericMethod. Good.
Now, once you've managed to invoke the method, there's then the matter of making an appropriate anonymous type. I would suggest you create a generic type and use Type.MakeGenericType on the generic type definition, then construct an instance of that. Alternatively, you could have another generic method which is strongly typed, and invoke that with reflection in a similar way. That method could use an anonymous type:
public object CreateResult<TTable, TJoin>(TTable t, TJoin joinTable)
{
return new { FromT = t, InnerT = joinTable };
}
You could call your generic MethodInfo helper to make it easier to invoke this.
I Auto resolve my solutions, inspired from Jon Skeet answer.
I create a class generic
public class ConvertTable<TOuter, TInner>
where TInner : class
where TOuter : class
{
public TOuter FromT { get; set; }
public TInner InnerT { get; set; }
public ConvertTable(TOuter fromT, TInner innerT)
{
FromT = fromT;
InnerT = innerT;
}
}