【问题标题】: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 是不正确的。例如,如果 leftClassObject 类型,rightClassMyClass 类型(继承自 Object),那么这就是我认为代码的工作方式......

  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 的 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 { }
    

    您的方法会说 AB 不相关并返回 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());
}

【讨论】:

  • 这不会“总是产生一个通用类型”。尝试使用不相交的接口。
  • 什么是“脱节”接口?可以举个例子吗?
  • 在这种情况下,两个不相交的接口是两个接口,它们都不继承另一个。例如。 IComparableIEnumerable 是不相交的,而 IQueryableIEnumerable 不是。明白了吗?
  • 我明白了。谢谢你。我的(更新的)代码产生相应的。 nullIEnumerable。您的代码产生相应的。 objectobject.
  • 我想指出,我们在如何处理接口和空参数方面做出了不同的设计决定。你选择返回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
相关资源
最近更新 更多