簡體   English   中英

如何根據約束條件比較通用類型

[英]How to compare generic types based on constraints

假設我有兩個班,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因為它們沒有約束。 TThrid不相等,因為它們沒有相同的約束。

所以現在我有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== ,跟隨Equality比較器始終返回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;
    }
}

意外的行為來自

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM