【问题标题】:Compare objects and determine common type比较对象并确定常见类型
【发布时间】:2013-05-02 00:09:55
【问题描述】:

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

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

我在我正在处理的代码库中遇到了这段代码,我认为 cmets 不正确。我相信 cmets //rightClass is a subclass of leftClass 和 //leftClass is a subclass of rightClass 是不正确的。例如,如果 leftClass 是 Object 类型,rightClass 是 MyClass 类型(继承自 Object),那么这就是我认为代码的工作方式......

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

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

【问题讨论】:

  • 请告诉我你要在上面打一个 [Obsolete("DO NOT USE")]!
  • 我添加了我遗漏的代码。代码中发生的事情比我在这里提出的要多……但这不适用于我的问题。到目前为止,我真的很欣赏下面的 cmets 和代码......但我认为我的问题尚未得到解答:我对代码的理解是否正确而 cmets 不正确?
  • @Steve, leftClass.IsInstanceOfType(right) 表示“右边是左边”。 rightClass.IsInstanceOfType(left) 表示“左为右”。如果右边是左边,但左边不是右边,那么左边一定是超类,右边一定是子类。这相当于第一种情况,您有注释“rightClass 是 leftClass 的子类”。
  • 看来,cmets的意图其实是准确的

标签: c# .net inheritance types


【解决方案1】:

leftClass.IsInstanceOfType(right) 表示“右为左”。 rightClass.IsInstanceOfType(left) 表示“左为右”。

如果右边是左边,但左边不是右边,那么左边一定是超类,右边一定是子类。这相当于第一种情况,您有注释“rightClass 是 leftClass 的子类”。所以,看起来cmets的意图其实是准确的。

但是,我发现该方法存在一些问题。 cmets 是您最不必担心的问题。

  1. 如果任一参数为空,它将抛出NullReferenceException。

  2. 它对GetType() 进行了不必要的调用,因为IsInstanceOfType 的实际源代码如下所示:

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

    您应该接受@p.s.w.g. 的建议并使用IsAssignableFrom,并可能考虑重构签名比较两个类型而不是两个对象。

  3. 任何两个具体类型总是至少有一个共同的基本类型System.Object。此处返回 null 不是可接受的结果。

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

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

    您的方法会说 A 和 B 不相关并返回 null,其中正确的“公共基础”将是 Base。

我会查看Easiest way to get a common base class from a collection of types 提供的实现,它仍然不完美,但比您发布的要好。

更新:工作代码

我决定不吝啬,我已经在下面发布了我为此目的使用的方法。祝你好运。

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

    }

【讨论】:

  • 我感觉你的示例代码有点臃肿的目的......结果与我的示例代码有什么不同?
  • @MartinMulder,(1) 我们的方法处理接口的方式不同。 (2) 您的代码无法处理我在回答中第 4 点描述的情况
  • @MartinMulder,(3)您的操作是二进制的。我的是n进制。 (这很可能是“为目的而膨胀”的一个例子,但它确实产生了一种更通用、可重用的方法)
  • 我的代码确实处理了第 4 点。我的函数很容易转换成两个以上的类型参数。我将在接口上工作:)
  • 在您的示例中,如果有任何接口,您的代码将返回object。我认为这很容易。 ICollection&lt;int&gt; 和 IList&lt;int&gt; 应该返回 ICollection&lt;int&gt;。而List&lt;int&gt; 加上IEnumerable&lt;int&gt; 应该返回IEnumerable&lt;int&gt;。
【解决方案2】:

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

【讨论】:

  • 这不会“总是产生一个通用类型”。尝试使用不相交的接口。
  • 什么是“脱节”接口?可以举个例子吗?
  • 在这种情况下,两个不相交的接口是两个接口,它们都不继承另一个。例如。 IComparable 和 IEnumerable 是不相交的,而 IQueryable 和 IEnumerable 不是。明白了吗?
  • 我明白了。谢谢你。我的(更新的)代码产生相应的。 null 和 IEnumerable。您的代码产生相应的。 object 和 object.
  • 我想指出,我们在如何处理接口和空参数方面做出了不同的设计决定。你选择返回null,我选择返回typeof(object)。我并不是说一种方式更正确,但我会解释一下我的推理:在类型继承方面,任何具体对象都将派生自System.Object,因此返回System.Object可以看作是@的实现987654321@。这样做的好处是客户端代码不需要对结果进行空检查以避免错误,并且会
猜你喜欢
  • 1970-01-01
  • 2010-10-17
  • 2017-06-01
  • 1970-01-01
  • 2015-06-09
  • 2021-12-31
  • 1970-01-01
  • 1970-01-01
  • 2023-03-11
相关资源
最近更新 更多