【问题标题】:How to compare generic types based on constraints如何根据约束比较泛型类型
【发布时间】:2017-10-14 19:50:17
【问题描述】:

假设我有两个类,Foo 和 Bar。

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

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

现在我想比较它们的泛型类型。我正在使用相等比较器对类型数组进行操作。比如相交、相减等。

我不想比较FooBar,但我想比较它们的通用参数。

例如,如果两个类型参数具有相同的约束,则它们应该被视为相等。如果他们没有约束,他们也应该被认为是平等的。

在上面的例子中,Foo 的 TFirst 应该被认为等于 Bar 的 TFirst。以及TSecond,因为它们没有约束。 TThrids 不相等,因为它们没有相同的约束。

所以现在我有 Foo 和 Bar 类型。我想分析它们的类型参数并将它们相互比较。

var fooType = typeof(Foo<,,,>);
var barType = typeof(Bar<,,,>);

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

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

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

无论我使用IsAssignableFrom 还是==,遵循平等比较器总是返回false。这样做的正确方法是什么?

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

【问题讨论】:

  • 您的代码不会编译,因为 T 没有定义。你是说TSecond
  • @BRAHIMKamel 你可以添加另一个类型参数T。对不起,我错过了。它们只是示例
  • @BRAHIMKamel 的重点是,因为T 没有约束,所以无论取自 Foo 还是 Bar 都应该被认为是相等的。如果定义了约束,它们必须相同才能被视为相等。

标签: c# generics reflection type-inference


【解决方案1】:

意外的行为来自于

cons.IsAssignableFrom

您不能尝试将开放的泛型类型从派生分配到基类,即使分层可能 以此为例

var ien = typeof(IEnumerable<string>);
            var iread = typeof(IReadOnlyList<string>);
            //isAssignable will be true 
            var isAssignable = ien.IsAssignableFrom(iread);
            //here because IsAssignableFrom work from BaseType.IsAssignableFrom(DerviedType)       
            var isAssignableIEn = iread.IsAssignableFrom(ien);

还有这个

var ien = typeof(IEnumerable<>);
            var iread = typeof(IReadOnlyList<>);
            var isAssignable = ien.IsAssignableFrom(iread);
            //here because IsAssignableFrom work from BaseType.IsAssignableFrom(DerviedType)       
            var isAssignableIEn = iread.IsAssignableFrom(ien);

两个赋值检查都将是false 这是预期的行为,因为默认情况下无法实例化开放的泛型类型,因此不可赋值

要创建开放泛型类型的实例,您应该使用Type.MakeGenericType

为了解决您的问题,这可能会对您有所帮助

public class GenericArgumentEqualityComparer : IEqualityComparer<Type>
    {
        public bool Equals(Type x, Type y)
        {

            var xInterfacesTypes = x.GetInterfaces();
            var yInterfacesTypes = y.GetInterfaces();
            if (!xInterfacesTypes.Any()&&!yInterfacesTypes.Any() )
            {
                return true; 
            }
            if ((!xInterfacesTypes.Any() && yInterfacesTypes.Any()) || xInterfacesTypes.Any() && !yInterfacesTypes.Any())
            {
                return false; 
            }          
            foreach (var xInterfacesType in xInterfacesTypes)
            {
                var iType = xInterfacesType.IsGenericType ? xInterfacesType.GetGenericTypeDefinition() :xInterfacesType;
                var yType = yInterfacesTypes.Any(yI => yI.IsGenericType && yI.GetGenericTypeDefinition() == iType||yI.GetType()==xInterfacesType.GetType());
                if (!yType)
                {
                    return false;
                }

            }
            return true; 
        }

        public int GetHashCode(Type obj)
        {
            return obj.Name.GetHashCode(); 
        }
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-08-10
    • 2017-01-21
    • 1970-01-01
    • 2010-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多