【问题标题】:Comparing object of type "Object" in C#在 C# 中比较“对象”类型的对象
【发布时间】:2023-03-11 15:10:01
【问题描述】:

我正在尝试在C# 中实现一个树形数据结构,我已经在C++ 中完成了 100 次 但是在这里,当我试图保持 Tree 通用以接受任何数据类型时,我已将每个 Node 的数据放在 Object 类型的对象中,如下所示:

    public class Node
    {
        public object Data;
        public Node Right;
        public Node Left;

        public Node(object value)
        {
            NodeContent = value;
            Right = Left = null;
        }
    }

但是当我尝试实现insert 时,我需要比较Object 类型的两个对象,以便知道是在当前节点的左侧还是右侧插入一个新节点。当我尝试检查时

    if(value < childPtr.Data)

其中value 是要插入的值,我收到一条错误消息,提示我无法比较Object 类型的两个对象。那么有没有办法解决这个问题?

【问题讨论】:

  • 如果你想让树泛型,那么你应该使用泛型!将其设为class Node&lt;T&gt; where T : IComparable&lt;T&gt; 并使用public T Data;。然后你可以做value.CompareTo(childPtr.Data)
  • 当你说value &lt; childPtr.Data时,你已经在暗示对象是一个值类型,那为什么还要使用object呢?
  • @AlexanderDerck 我使用了对象,所以我可以保持 Data 变量的通用性,因此树将接受任何数据类型。
  • 如果我创建两个新对象,var o1 = new object(); var o2 = new object();... 其中哪个比另一个“更大”?我插入一个浮点数,然后是一个字符串 - 你会如何比较它们?如果您想通用地执行此操作,则应使用泛型...此时您可以添加T实现IComparable&lt;T&gt;的约束...

标签: c# data-structures tree binary-search-tree


【解决方案1】:

您的方法的问题是Data 可以是任何类型。您的设计允许您将多种类型放入同一棵树中。所以一个节点可能有一个string,另一个可能有一个double,第三个可能有一个用户定义类型的引用。如果您让Node 构造函数采用IComparable 实例,那么您假设树中的所有项目都属于同一类型,或者它们的IComparable 接口实现知道如何比较所有可能的值数据类型。

简而言之,虽然你所做的会起作用,但它根本不是类型安全的。

您所要做的是非常类似于 C 的事情。在 C++ 中,您将使用模板来避免这种可憎的事情。在 C# 中,您使用泛型。

我赞同 cmets 中提出的建议:如果您想要一个通用数据结构,制作一个通用数据结构。使用内置的集合,例如,如果你想要一个你写的整数列表:

var list_of_integers = new List<int>();

如果你想要一个字符串列表:

var list_of_strings = new List<string>();

如果你想创建一个通用的树集合,你可以从:

public class MyGenericTree<T>
{
    public class Node
    {
        public T Data;
        public Node Left;
        public Node Right;

        public Node(T data)
        {
            Data = data;
        }
    }

    private readonly IComparer<T> _comparer;

    public MyGenericTree(IComparer<T> comparer = null)
    {
        _comparer = comparer ?? Comparer<T>.Default;
    }
}

然后你创建它:

var myTree = new MyGenericTree<string>(); // or int, or whatever type

如果你想要一个自定义的比较函数,你写:

// Create a tree that stores case-insensitive strings
var myTree = new MyGenericTree<string>(StringComparer.CurrentCultureIgnoreCase);

这会强制Data 始终为兼容类型。您要么使用该类型的默认比较器,要么使用传递给构造函数的比较器接口。

在进行比较时,它是:

int a = _comparer.Compare(node1, node2);
if (a < 0)
    // node1 < node2
else if (a > 0)
    // node1 > node2
else
    // node1 == node2

如果您确实想在树中存储无类型的 object 引用,您可以轻松编写:

var myTree = new MyGenericTree<object>(some_object_comparer);

虽然你为什么要做这样的事情有点神秘。

我知道泛型一开始看起来有点奇怪,但是在使用它们一天左右之后,你就会明白它们非常灵活并且类型安全。

【讨论】:

  • 我非常同意吉姆的观点。我的答案只是快速修复,而吉姆则导致了更好的方法。泛型非常适合所描述的问题。
  • @Jim Mischel 谢谢,这实际上更有意义。感谢您的帮助。
【解决方案2】:

由于您需要从数据中进行比较,因此更好的方法是:

public class Node
{
    public IComparable Data;
    public Node Right;
    public Node Left;

    public Node(IComparable value)
    {
        Data = value;
        Right = Left = null;
    }
}

以后你可以这样做:

if(value.CompareTo(childPtr.Data) < 0)

【讨论】:

  • 我猜 CompareTo() 在它们相等时返回 0,当 value 小于 Data 时返回小于 0,当 Data 大于 value 时返回大于 0。我说的对吗?
  • 我真的很想否决这个答案,不是因为它不正确,而是因为它延续了一种非常糟糕的开发实践。不幸的是,尽管它是构建“通用”数据结构问题的可怕解决方案,但它解决了 OP 的直接问题。不过,我确实想强调,劝阻 OP 不要继续走他的错误道路会更好。
猜你喜欢
  • 2010-10-17
  • 2014-06-13
  • 2011-07-09
  • 1970-01-01
  • 1970-01-01
  • 2021-12-31
  • 1970-01-01
  • 2010-10-16
相关资源
最近更新 更多