比较对象并确定公共类型

本文关键字:类型 对象 比较 | 更新日期: 2023-09-27 18:11:17

简短版本:代码中的注释是否正确?我认为它们是不正确的,应该颠倒过来。

public Type GetTestClass(Object left, Object right)
{
    if (left == null || right == null) return null;
    Type leftClass = left.GetType();
    Type rightClass = right.GetType();
    Type testClass;
    if (leftClass.IsInstanceOfType(right))
    {
        testClass = leftClass;
        if (!rightClass.IsInstanceOfType(left))
        {
            // rightClass is a subclass of leftClass
            testClass = rightClass;
        }
    }
    else if (rightClass.IsInstanceOfType(left))
    {
        testClass = rightClass;
        if (!leftClass.IsInstanceOfType(right))
        {
            // leftClass is a subclass of rightClass
            testClass = leftClass;
        }
    }
    else
    {
        // The two classes are not related.
        testClass = null;
    }
return testClass;
}

我在我正在处理的代码库中遇到了这段代码,我认为注释是不正确的。我认为评论//rightClass is a subclass of leftClass//leftClass is a subclass of rightClass是不正确的。例如,如果leftClassObject类型,rightClassMyClass类型(继承自对象),那么这就是我认为代码的工作方式…

  1. ObjectMyClass为FALSE的实例
  2. MyClassObject是TRUE的一个实例
  3. testClass设置为MyClass
  4. NOT (ObjectMyClass为FALSE的一个实例)为TRUE
  5. testClass设置为Object
  6. testClass返回== Object

我相信这是正确的功能…最终,代码试图返回两个类中最大的类(也可以说是公共基类),如果它们不相关,则返回null。使用上面的例子(步骤1-5),是不是真的说leftClass是rightClass的超类…或者反过来说,rightClass是leftClass的子类?

比较对象并确定公共类型

leftClass.IsInstanceOfType(right)表示"右为左"。rightClass.IsInstanceOfType(left)表示" left is a right"

如果右是左,但左不是右,则左必须是父类,而右必须是子类。这相当于第一种情况,你有注释"rightClass是leftClass的子类"。所以,看起来评论的意图实际上是准确的。

然而,我看到了这个方法的一些问题。评论是你最不必担心的。

  1. 如果参数为null,将抛出NullReferenceException

  2. 它对GetType()进行了不必要的调用,因为IsInstanceOfType的实际源代码看起来像这样:

    public virtual bool IsInstanceOfType(object o)
    {
        if (o == null)
        {
            return false;
        }
        return this.IsAssignableFrom(o.GetType());
    }
    

    你应该采取@p.s.w.g。

  3. 参考IsAssignableFrom的建议,并可能考虑重构签名比较两个类型而不是两个对象。
  4. 任何两个具体类型总是至少有一个共同的基础类型System.Object。返回null是不可接受的。

  5. 它不处理一种类型不是线性派生自另一种类型的情况,但两者仍然有一个比System.Object更派生的公共基类。例如,

    public class Base { }
    public class A : Base { }
    public class B : Base { }
    

    你的方法会说AB是不相关的,并返回null,其中正确的"公共基础"将是Base

我会看看实现提供了最简单的方法,从类型集合中获得一个公共基类,这仍然是不完美的,但比你发布的一个更好。

更新:工作代码

我决定不吝啬,我把我用于这个目的的方法贴在下面。好运。

    /// <summary> Finds the most derived common base class of all the provided types, or System.Object if there is no common base class  </summary>
    public static Type CommonBaseClass(params Type[] types)
    {
        if(ReferenceEquals(types,null)) return typeof(object);
        types = types.Where(x => !ReferenceEquals(x,null)).Distinct().ToArray();
        switch (types.Length)
        {
            case 0: return typeof(object);
            case 1: return types[0].IsInterface ? typeof(object): types[0];
            default:
                IEnumerable<IEnumerable<Type>> hierarchies = types.Select(ClassHierarchy).OrderBy(x => x.Count());
                Queue<Type> smallest = new Queue<Type>(hierarchies.First().Reverse());
                hierarchies = hierarchies.Skip(1);
                do
                {
                    int maxPossible = smallest.Count;
                    hierarchies = hierarchies.Select(each => each.Take(maxPossible));
                    Type candidate = smallest.Dequeue();
                    if (hierarchies.All(each => each.Last() == candidate))
                        return candidate;
                } while (smallest.Count > 1);
                return typeof(object);
        }
    }

    ///<summary>Gets the class hierarchy of the provided type, in order of derivation, e.g. : (System.Object,CustomBaseType,CustomConcreteType,...), or the singleton of System.Object type if the provided type is an interface or null </summary>
    public static IEnumerable<Type> ClassHierarchy(this Type type)
    {
        if (type == null || type.IsInterface) type = typeof(object);
        var stack = new Stack<Type>();
        do
        {
            stack.Push(type);
            type = type.BaseType;
        } while (type != null);
        return stack;
    }

EDIT:更新以反映处理接口类型的更改。
EDIT:添加了支持2种以上类型的额外功能。

首先:一个类型不能是另一个类型的实例(除非我们讨论的是Type类型的实例)。只有对象才能是类型的实例。

类型可以是彼此的子类型(使用IsSubType)。类型可以赋值给另一个类型(使用IsAssignableFrom)。

要查找通用类型,请尝试以下示例。这将总是产生一个公共类型(对象):

    /// <summary>
    /// Returns the most common type of two types.
    /// If no common type can be found, null is returned.
    /// </summary>
    static public Type GetCommonBaseClass(Type a, Type b)
    {
        if ((a == null) || (b ==null))
            return null;
        if (a.IsInterface || b.IsInterface)
            return null;
        if (a.IsAssignableFrom(b))
            return a;
        while (true)
        {
            if (b.IsAssignableFrom(a))
                return b;
            b = b.BaseType;
        }
    }
    /// <summary>
    /// Returns the most common type of one or more types.
    /// If no common type can be found, null is returned.
    /// </summary>
    static public Type GetCommonBaseClass(params Type[] types)
    {
        if ((types == null) || (types.Length == 0))
            return null;
        Type type = types[0];
        for (int i = 0; i < types.Length; i++)
            type = GetCommonBaseClass(type, types[i]);
        return type;
    }

从你的函数中调用:

public Type GetTestClass(Object left, Object right)
{
     return GetCommonBaseClass(left.GetType(), right.GetType());
}