samedi 14 octobre 2017

How to compare generic types based on constraints

Assume that I have two classes, Foo and Bar.

public class Foo<TFirst, TSecond, TThird> 
       where TFirst : IReadOnlyList<T>
       where TThird : IEnumerable<T>
{
}

public class Bar<TFirst, TSecond, TThird> 
       where TFirst : IReadOnlyList<T>
{
}

Now I want to compare their generic types. I'm using equality comparer to operate on array of types. like Intersect, Subtract etc.

I don't want to compare Foo and Bar but I want to compare their Generic parameters.

for example if both type parameters have same constraint, they should be considered equal. if they have no constrained they should be considered equal too.

In above example TFirst of Foo should be considered equal to TFirst of Bar. as well as TSecond because they have no constraint. TThrids are not equal because they don't have same constraint.

So now I have Foo and Bar types. I want to analyze their type arguments and compare them against each other.

var fooArgs = fooType.GetGenericArguments();
var barArgs = barType.GetGenericArguments();

var commonArgs = fooArgs.Intersect(barArgs, new GenericArgumentEqualityComparer()).ToArray();

var unknownBarArgs = barArgs.Except(commonArgs, new GenericArgumentEqualityComparer()).ToArray();

Following Equality comparer always returns false, no matter I use IsAssignableFrom or ==. what is the right way to do this?

public class GenericArgumentEqualityComparer : IEqualityComparer<Type>
{
    public bool Equals(Type x, Type y)
    {
        if (x == null || y == null) return false;
        var xcons = x.GetGenericParameterConstraints();
        var ycons = y.GetGenericParameterConstraints();

        if(xcons.Length != ycons.Length) return false;

        foreach (var cons in xcons)
        {
            if (ycons.All(cons2 => !cons.IsAssignableFrom(cons2)))
                return false;
        }
        return true;
    }

    public int GetHashCode(Type obj)
    {
        // code runs on T4 for code generation. performance doesn't matter.
        return 0;
    }
}





Aucun commentaire:

Enregistrer un commentaire