Determine if two different PropertyInfo come from the same interface? - c#

I've been using
public static bool IsSameAsProperty(PropertyInfo first, PropertyInfo second) =>
first.DeclaringType == second.DeclaringType && first.Name == second.Name;
To determine whether a reflected property info matches some property I grabbed off a base class.
This approach has begun falling apart when I try to reference properties defined in interfaces.
For example, imagine the following multiple-interface inheritance scenario:
interface IAnimal : { bool IsHungry { get; } }
interface IDog : IAnimal { }
abstract class Animal : IAnimal { public bool IsHungry { get; set; } }
class Dog : Animal, IDog { }
If I'm creating property expressions, all of the following are valid:
Expression<Func<object, bool>> propertyExpression;
propertyExpression = (IAnimal animal) => animal.IsHungry
propertyExpression = (Animal animal) => animal.IsHungry
propertyExpression = (IDog dog) => dog.IsHungry
propertyExpression = (Dog dog) => dog.IsHungry
Since each of these types define or inherit the property IsHungry, all these expressions are valid. One might even argue they are all referring to the same property (although I can appreciate the subtle differences between an interface and instance declaration).
My problem is that I want some way of programatically detecting that all of these properties "come from" the shared interface IAnimal and are compatible. Unfortunately, my test returns false because:
IDog.IsHungry has DeclaringType == typeof(IAnimal) whereas
Dog.IsHungry has DeclaringType == typeof(Animal)
I can't think of an easy way to compare interface and concrete type property expressions without either resorting to a simple Name comparison (which is prone to false-positives) - but I can't think of anything that doesn't involve enumerating all the interfaces inherited by the two types and looking for anything with that property name that's in both sets.
Q: Can we create a function that returns true when comparing any of the PropertyInfo yielded from the above 4 property expressions. (e.g. Detect that they all represent/implement the same base interface property?)

This will probably lead to false positives in the case of the new keyword being used to hide inherited properties, but:
public static bool IsSameAsProperty(PropertyInfo first, PropertyInfo second) =>
first == second || // If default equals implementation returns true, no doubt
first.Name == second.Name && (
first.DeclaringType == second.DeclaringType ||
first.DeclaringType.IsAssignableFrom(second.DeclaringType) ||
second.DeclaringType.IsAssignableFrom(first.DeclaringType));
I suppose I could be a bit more specific and check if first.DeclaringType.IsInterface and vice-versa, but it's still possible to implement that interface explicitly and hide its property with a new one with the same name, so the extra check wouldn't make this any 'safer'.
Not sure if I can tell whether one PropertyInfo instance represents the interface implementation of the other.

The solution I came up with compares the getter/setter MethodInfo to the InterfaceMapping. It passes all tests I could think of, but I'm not 100% convinced that there aren't weird corner cases this doesn't catch.
public static bool IsSameAsProperty(PropertyInfo first, PropertyInfo second)
{
if (first.DeclaringType == second.DeclaringType && first.Name == second.Name)
return true;
bool firstIsSecond = second.DeclaringType.IsAssignableFrom(first.DeclaringType);
bool secondIsFirst = first.DeclaringType.IsAssignableFrom(second.DeclaringType);
if (firstIsSecond || secondIsFirst)
{
PropertyInfo baseProp = firstIsSecond ? second : first;
PropertyInfo derivedProp = firstIsSecond ? first : second;
MethodInfo baseMethod, implMethod;
if (baseProp.GetMethod != null && derivedProp.GetMethod != null)
{
baseMethod = baseProp.GetMethod;
implMethod = derivedProp.GetMethod;
}
else if (baseProp.SetMethod != null && derivedProp.SetMethod != null)
{
baseMethod = baseProp.SetMethod;
implMethod = derivedProp.SetMethod;
}
else
{
return false;
}
// Is it somehow possible to create a situation where both get and set exist
// and the set method to be an implementation while the get method is not?
if (baseMethod.DeclaringType.IsInterface)
return IsInterfaceImplementation(implMethod, baseMethod);
else
return IsOverride(implMethod, baseMethod);
}
return false;
}
private static bool IsInterfaceImplementation(MethodInfo implMethod, MethodInfo interfaceMethod)
{
InterfaceMapping interfaceMap = implMethod.DeclaringType.GetInterfaceMap(interfaceMethod.DeclaringType);
int index = Array.IndexOf(interfaceMap.InterfaceMethods, interfaceMethod);
// I don't think this can ever be the case?
if (index == -1)
return false;
MethodInfo targetMethod = interfaceMap.TargetMethods[index];
return implMethod == targetMethod || IsOverride(implMethod, targetMethod);
}
private static bool IsOverride(MethodInfo implMethod, MethodInfo baseMethod)
{
return implMethod.GetBaseDefinition() == baseMethod.GetBaseDefinition();
}

Related

How to compare two IEnumerable<T> in C# if I don't know the actual object type?

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.

How to compare two types of classes which receives generics? [duplicate]

I've got a generic method:
Func<IEnumerable<T>, bool> CreateFunction<T>()
where T can be any number of different types. This method does a bunch of stuff using reflection and if T is an IDictionary, regardless of the the dictionary's TKey and TValue I need to execute dictionary specific code.
So the method could be called:
var f = CreateFunction<string>();
var f0 = CreateFunction<SomePocoType>();
var f1 = CreateFunction<IDictionary<string,object>>();
var f2 = CreateFunction<Dictionary<string,object>>();
var f3 = CreateFunction<SomeDerivedDictionaryType<string,object>>();
etc.
Clarification per #Andy's answer
Ultimately I want to know if T inherits from/implements IDictionary even if T itself is Dictionary or some other type that derives from that interface.
if(typeof(T) == typeof(IDictionary<,>)
doesn't work because T is the generic type not the generic type definition.
And without knowing TKey and TValue (which are not known at compile time) I can't do a comparison to any concrete type that I would know about until runtime.
The only thing that I've come up with are looking at the type's name or inspecting its method with reflection, looking for methods that would lead me to believe it is a dictionary (i.e. look for ContainsKey and get_Item).
Is there any straightforward way to make this sort of determination?
You can avoid using ugly and potentially risky type name string checking using the IsGenericType and GetGenericTypeDefinition members, as follows:
var type = typeof (T);
if (typeof (IDictionary).IsAssignableFrom(type))
{
//non-generic dictionary
}
else if (type.IsGenericType &&
type.GetGenericTypeDefinition() == typeof (IDictionary<,>))
{
//generic dictionary interface
}
else if (type.GetInterfaces().Any(
i => i.IsGenericType &&
i.GetGenericTypeDefinition() == typeof (IDictionary<,>)))
{
//implements generic dictionary
}
The easy way is just this:
Type iDict = null;
if (typeof(T).GetGenericTypeDefinition() == typeof(IDictionary<,>))
iDict = typeof(T);
else
iDict = typeof(T).GetInterface(typeof(IDictionary<,>).Name);
if (iDict != null)
{
var genericParams = iDict.GetGenericArguments();
Type tKey = genericParams[0], tValue = genericParams[1];
}
Note that this will not work (throws an exception) if T implements more than one IDictionary<,> interface, but that will probably be fine for your purposes.
For the sake of completeness, here's an implementation that will work on types with multiple IDictionary<,> interfaces by using the first one:
Type iDict = t.GetType().GetInterfaces()
.Where(t => t.IsGenericType
&& t.GetGenericTypeDefinition() == typeof(IDictionary<,>))
.FirstOrDefault();
if (iDict != null)
{
var genericParams = iDict.GetGenericArguments();
Type tKey = genericParams[0], tValue = genericParams[1];
}
Note that in this second routine t is an object, whereas T is a type in the first routine.
You could do something like
class Program
{
static void Main(string[] args)
{
Example<IDictionary<int, string>>.IsDictionary();
Example<SortedDictionary<int, string>>.IsDictionary();
Example<Dictionary<int, string>>.IsDictionary();
Console.ReadKey();
}
}
public class Example<T>
{
public static void IsDictionary()
{
if (typeof(T).GetInterface(typeof(IDictionary<,>).Name) != null || typeof(T).Name.Contains("IDictionary"))
{
Console.WriteLine("Is IDictionary");
}
else
{
Console.WriteLine("Not IDictionary");
}
}
}
I think that if you call Type.GetGenericTypeDefinition() that should return the "base" generic type used to construct the concrete Type.
Note that just comparing this to IDictionary<,> is likely not enough, because if someone passes in an instance of Dictionary<,> I assume you would want to use that, as well. You could either check to see if the Type implements IDictionary<,> or you might be able to call Type.IsAssignableFrom(), although based on the doc I'm not sure how well this would work with generic Types.

Getting all ICollection properties through reflection

I'm trying to fetch all ICollection<T> properties from class of unknown type. Also, type T (what the collection is of) is not known at compile time. Firstly I've tried this approach:
foreach (var property in entity.GetType().GetProperties())
{
if (typeof(ICollection).IsAssignableFrom(property.PropertyType) || typeof(ICollection<>).IsAssignableFrom(property.PropertyType))
{
// do something
}
}
but it's not working (evaluating false even for ICollection properties).
I got it working like this:
foreach (var property in entity.GetType().GetProperties())
{
var getMethod = property.GetGetMethod();
var test = getMethod.Invoke(entity, null);
if (test is ICollection)
{
// do something
}
}
but I do not want to execute all getters. Why is the first piece of code not working? How can I find ICollection properties without executing all getters?
It turns out that with IsAssignableFrom check you can't find whether the interface is a derivative of another interface:
Console.WriteLine(typeof(ICollection<>).IsAssignableFrom(typeof(ICollection<Int32>)));
Console.WriteLine(typeof(ICollection<Int32>).IsAssignableFrom(typeof(ICollection<>)));
will both write false;
With little help from here this is the best solution I can come of:
static IEnumerable<PropertyInfo> GetICollectionOrICollectionOfTProperties(this Type type)
{
// Get properties with PropertyType declared as interface
var interfaceProps =
from prop in type.GetProperties()
from interfaceType in prop.PropertyType.GetInterfaces()
where interfaceType.IsGenericType
let baseInterface = interfaceType.GetGenericTypeDefinition()
where (baseInterface == typeof(ICollection<>)) || (baseInterface == typeof(ICollection))
select prop;
// Get properties with PropertyType declared(probably) as solid types.
var nonInterfaceProps =
from prop in type.GetProperties()
where typeof(ICollection).IsAssignableFrom(prop.PropertyType) || typeof(ICollection<>).IsAssignableFrom(prop.PropertyType)
select prop;
// Combine both queries into one resulting
return interfaceProps.Union(nonInterfaceProps);
}
This solution may yield some duplicates(it is hardly possible, but to be sure use Distinct) and it doesn't look very nice.
But it works well on such class with properties with both the interface return types and concrete return types :
class Collections
{
public List<Int32> ListTProp
{
get;
set;
}
public IDictionary<Int32, String> IDictionaryProp
{
get;
set;
}
public ICollection ICollectionProp
{
get;
set;
}
public ICollection<DateTime> IDateTimeCollectionProp
{
get;
set;
}
}
After attempting to use the accepted answer I had a situation where only partial matches where being returned. My object had 3 ICollection<T> properties and I was only being returned with 2. I spent some time testing and trying to figure out why, but I moved on and wrote this:
public static IEnumerable<PropertyInfo> GetICollectionProperties(object entity)
{
return entity.GetType().GetProperties()
.Where(p => p.PropertyType.IsGenericType
&& p.PropertyType.GetGenericTypeDefinition() == typeof(ICollection<>));
}
I have tested with the same test cases and I am getting the correct results returned from this method.
This will not pick up non-generic ICollections, but the OP did ask for ICollection<T>properties, all-though it could be easily re-factored to include. It will also not return properties that are not exactly of type ICollection (ie, Eugene's List and IDictionary in his test case would not be returned (but again, what the OP wanted)).

How to determine if a type is a type of collection?

I am trying to determine if a runtime type is some sort of collection type. What I have below works, but it seems strange that I have to name the types that I believe to be collection types in an array like I have done.
In the code below, the reason for the generic logic is because, in my app, I expect all collections to be generic.
bool IsCollectionType(Type type)
{
if (!type.GetGenericArguments().Any())
return false;
Type genericTypeDefinition = type.GetGenericTypeDefinition();
var collectionTypes = new[] { typeof(IEnumerable<>), typeof(ICollection<>), typeof(IList<>), typeof(List<>) };
return collectionTypes.Any(x => x.IsAssignableFrom(genericTypeDefinition));
}
How would I refactor this code to be smarter or simpler?
Really all of these types inherit IEnumerable. You can check only for it:
bool IsEnumerableType(Type type)
{
return (type.GetInterface(nameof(IEnumerable)) != null);
}
or if you really need to check for ICollection:
bool IsCollectionType(Type type)
{
return (type.GetInterface(nameof(ICollection)) != null);
}
Look at "Syntax" part:
List<T>
IList
ICollection
If you need to exclude strings (which are essentially an IEnumerable<char>), use the following function:
bool IsEnumerableType(Type type)
{
return (type.Name != nameof(String)
&& type.GetInterface(nameof(IEnumerable)) != null);
}
You can use this helper method to check if a type implements an open generic interface. In your case you can use DoesTypeSupportInterface(type, typeof(Collection<>))
public static bool DoesTypeSupportInterface(Type type,Type inter)
{
if(inter.IsAssignableFrom(type))
return true;
if(type.GetInterfaces().Any(i=>i. IsGenericType && i.GetGenericTypeDefinition()==inter))
return true;
return false;
}
Or you can simply check for the non generic IEnumerable. All collection interfaces inherit from it. But I wouldn't call any type that implements IEnumerable a collection.
You can use linq, search for an interface name like
yourobject.GetType().GetInterfaces().Where(s => s.Name == "IEnumerable")
If this has values is a instance of IEnumerable.
This solution will take care of ICollection and ICollection<T>.
static bool IsCollectionType(Type type)
{
return type.GetInterfaces().Any(s => s.Namespace == "System.Collections.Generic" && (s.Name == "ICollection" || s.Name.StartsWith("ICollection`")));
}
This work for me.
private static bool IsCollectionType(Type type)
{
return type.GetInterfaces().Any(s => s.Namespace == "System.Collections.Generic" && (s.Name == "IEnumerable" || s.Name.StartsWith("IEnumerable`")));
}

Linq .SingleOrDefault - how to setup a default for a custom class?

i went over some questions and searched google a bit,
but i couldnt find an answer ( That satisfies me ).
Basicly, i understand the SingleOrDefault return null or 0 ( depends on the type ).
but how can i make it return something else ?
return myChannels.All.Where(_Channel => _Channel.Guid == this.ParentChannelGuid).SingleOrDefault(_SPECIFICCHANNEL);
so, i want _SPECIFICCHANNEL to be returned in case it is not single..
can that be done ?
You will have to make an extension method:
public static T SingleOr<T>(this IEnumerable<T> list, T defaultValue) where T : class
{
return list.SingleOrDefault() ?? defaultValue;
}
There is no other way. All classes default to null.
This can be accomplished in a rather simple way. If you create your own extension method that is more specific than the generic SingleOrDefault, then the compiler will prefer the more type-specific version. Here's an example that shows how to do that with a simple Person class (you can copy-paste it into LINQPad to quickly see the result):
public class Person
{
public string Name { get; set; }
public override string ToString()
{
return Name ?? "";
}
}
public static class PersonExtensionMethod
{
public static Person SingleOrDefault(this IEnumerable<Person> source)
{
var person = Enumerable.SingleOrDefault(source);
if (person == null)
return new Person { Name = "Unnamed" };
return person;
}
}
public static void Main()
{
var emptyCollection = new Person[0];
var nonEmptyCollection = new Person[] { new Person { Name = "Jack" } };
Debug.WriteLine("Empty collection: " + emptyCollection.SingleOrDefault());
Debug.WriteLine("Non-empty collection: " + nonEmptyCollection.SingleOrDefault());
}
In the above example, SingleOrDefault(IEnumerable<Person>), takes precedence over SingleOrDefault<T>(IEnumerable<T>) which is less specific.
Could you use DefaultIfEmpty() (psedo code follows) -
return myChannels.All.Where(_Channel => _Channel.Guid == this.ParentChannelGuid).DefaultIfEmpty(_SPECIFICCHANNEL).SingleOrDefault();
but how can i make it return something else ?
You cannot. You can make your own method — as shown by Oskar Kjellin — to return someting else, but SingleOrDefault will always behave as programmed, which means return the default value (null, 0) for an item.
Why not just use the "??" operator and say
return myChannels.SingleOrDefault(_Channel => _Channel.Guid == this.ParentChannelGuid) ??_SPECIFICCHANNEL;
You cannot define the default value of a type. It is always defined as null for reference types. For structs, it is an instance of the struct where all member fields in turn are set to their default values. For enums, it is always 0 (which may or may not be a defined value of the enum type in question)
Expanding on Oskar's answer of an extension method you can make that more generic so that it covers value as well as reference types with something like the below:
public static T SingleOrSpecifiedDefault<T>(this IEnumerable<T> enumerable, Expression<Func<T, bool>> singleOrDefault, T defaultValue) where T : IComparable
{
T singleValue = enumerable.SingleOrDefault(singleOrDefault.Compile());
if (singleValue == null || singleValue.CompareTo(default(T)) == 0)
{
return defaultValue;
}
return singleValue;
}
This allows you to specify the same LINQ expression that you'd use with SingleOrDefault as well as the defaultValue (which will be used if no match is found).

Categories