7

我问这个是因为似乎使用 Object 似乎是解决某些问题的一种简单方法,例如“我没有特定类型,所以使用 Object”等。

这让我感到好奇的原因还在于,我的一位同事告诉我,如果 .NET 是一个真正的面向对象平台,那么它就不必拥有像 Object 这样的所有类型。

因此,如果 .NET 没有 Object 类型,那么解决出现的问题以使其功能相同的替代方法是什么?

还需要注意的是,这并不是要抨击 .NET,因为我每天都在工作中使用它。只是想了解更多。

编辑:我记得的另一个注意事项是因为 Object 类型存在,它的影响会在整个 .NET 中产生涟漪。像 IEnumerable 一样存在,但也存在 IEnumerable<T>。在许多情况下,您必须同时实现事物的通用和非通用版本等。

4

9 回答 9

3

您的朋友可能使用动态语言(如 ruby​​ 或 python),对吧?

有很多人认为强类型语言应该被称为“面向类”而不是“面向对象”,因为所有可能的行为和多态性都需要在类定义中预先完成。当您的方法接受任何内容时,任何检查都是基于对象功能而不是其类完成的,您可以说这是一种更加面向对象的方法。

我对这个论点有点矛盾。编程界有一种愚蠢的信念,即无论问题或要求如何,OO 都是明确的。正因为如此,人们倾向于通过说“某某不是面向对象”来赢得争论,并且由于面向对象是好的同义词,他们赢了。尽管我认为静态语言使用起来很痛苦,但我认为将它们称为不是 OO 是一种不诚实的方式来表达你的观点。另一方面,任何能让程序员走出他的舒适区并学习一种新的做某事的方法(如果没有其他原因,然后赢得争论)的事情都不会是完全坏的。正如我所说,矛盾。:)

于 2010-09-08T18:56:57.163 回答
3

我想说解决的问题Object不是“我没有特定的类型,所以使用 Object”,而是“我真的不在乎这是什么类型;我只需要知道它是一个Object

于 2010-09-08T18:41:24.960 回答
3

它很方便,不仅可以用作“通用”类型,还可以用于反射、垃圾收集等。

其他语言 (C++) 可以不用,但我会毫不犹豫地说这使这些语言更加 OOP。

于 2010-09-08T18:43:02.650 回答
2

在强类型框架中,对象必须从某个地方开始。

该类型object不是为了提供“简单”的包罗万象的铸造选项,或者迫使您降低到最低公分母。它可以为对象提供绝对最一般的情况——参考比较的基础和方便的ToString()方法,等等。

于 2010-09-08T18:41:13.330 回答
2

请记住,继承表示“is-a”关系。.NET 中的每个类“都是一个(n)”对象。他们都有一个ToString方法。它们都有一个类型,您可以通过GetType. 这种关系和由此产生的功能共享是面向对象编程的基础。

于 2010-09-08T18:45:51.067 回答
1

我对这个主题并不是特别了解,但从我的角度来看,允许人们在不知道如何使用这些组件的情况下构建多态组件是很有用的。我的意思是什么?让我试着解释一下。

让我们以 .NET 框架的ArrayList类为例。在引入泛型之前,这是原始框架的一部分。该类的作者ArrayList试图提供一个有用的动态列表实现,但他们无法知道哪些类型的对象将被插入到列表中。他们使用 Object 类型来表示列表中的项目,因为它允许将任何类型的类添加到列表中。例如:

        ArrayList people = new ArrayList();
        people.Add(new Doctor());
        people.Add(new Lawyer());
        people.Add(new PetDetective());
        people.Add(new Ferrari()); // Yikes!

        // ...

        for (int i = 0; i < people.Count; i++)
        {
            object person = people[0];
            // ...
        }

现在,如果这是您自己的应用程序,并且您知道您的DoctorLawyerPetDetective类都派生自一个公共Person基类,那么理论上您可以基于Person类而不是Object类构建您自己的链表实现。ArrayList但是,当您已经有一个构建和测试的类时,这会带来很多额外的工作而没有什么好处。如果你真的想让它特定于你的Person基类,那么你总是可以创建一个ArrayList只接受-Person派生对象的包装类。

在 C++ 中,您可以使用“空指针”数据类型 ( ) 来完成基本相同的操作void*。但是,C++ 也支持模板(与泛型非常相似),这使得构建有用的组件变得更加容易,而无需了解将使用的其他类的详细信息。由于 C# 最初并不支持泛型,因此使用Object类型确实是构建通用多态组件供其他人使用的唯一方法。

于 2010-09-08T19:01:59.083 回答
1

如果对象派生层次结构没有一个统一的头部,那么如果不求助于动态类型,就不可能测试任意两个任意……呃……事物之间的相等性。

除此之外,我怀疑对象的功能也可以通过单独的接口(IEqualable 和 IConvertableToString)来处理。另一方面,对象的虚方法有时也很方便,尤其是 ToString,它可以被 IDE 或调试器在显示程序状态时使用。这真是一个实用的设计。

于 2010-09-08T18:50:26.593 回答
1

System.Object在这个问题上已经多次提到了可以用接口替换的概念,作为一项规则,所有类都实现了接口。虽然我认为这个想法是有效的,但最后我会说它不会给你带来任何东西。即使存在这样的接口,编译器如何真正确保类实现这些接口的问题仍然存在。

下面是一些示例代码,我在其中尝试探索没有System.Object类型的假设情况以及引擎盖下的实现以及这些接口的用法可能看起来如何。

// let's start off by defining interfaces to describe the various methods that are currently available from the System.Object class

public interface IEquatable
{
    bool Equals(IEquatable other);
}

public interface IHashCodeGenerator
{
    int GetHashCode();
}

public interface ITypeIdentifiable
{
    Type GetType();
}

public interface IConvertibleToString
{
    string ToString();
}

// This guy throws a wrench into things, because we can't privately (or "protectedly") implement an interface.
// This is discussed further below on the MyClass.MemberwiseClone method.
public interface IMemberwiseCloneable
{
}

// This class simply encapsulates similar functionality found within the System.Object class
public static class ClrInternals
{
    [MethodImpl(MethodImplOptions.InternalCall)]
    internal static extern bool Equals(IEquatable objA, IEquatable objB);

    [MethodImpl(MethodImplOptions.InternalCall)]
    internal static extern int GetHashCode(IHashCodeGenerator hashGenerator);

    [MethodImpl(MethodImplOptions.InternalCall)]
    internal static extern Type GetType(ITypeIdentifiable typedInstance);

    [MethodImpl(MethodImplOptions.InternalCall)]
    internal static extern IMemberwiseCloneable MemberwiseClone(IMemberwiseCloneable original);
}

// let's say that as a rule the compiler implicitly makes all classes implement these interfaces
class MyClassExampleA : IEquatable, IHashCodeGenerator, ITypeIdentifiable, IConvertibleToString, IMemberwiseCloneable
{
    // The compiler also implicitly makes all classes implement the interfaces with the following code (unless otherwise specified)

    #region IEquatable Members

    public bool Equals(IEquatable other)
    {
        // let's suppose that this is equivalent to the current implementation of Object.Equals
        return ClrInternals.Equals(this, other);
    }

    #endregion

    #region IHashCodeGenerator Members

    public int GetHashCode()
    {
        // let's suppose that this is equivalent to the current implementation of Object.GetHashCode
        return ClrInternals.GetHashCode(this);
    }

    #endregion

    #region ITypeIdentifiable Members

    public Type GetType()
    {
        // let's suppose that this is equivalent to the current implementation of Object.GetType
        return ClrInternals.GetType(this);
    }

    #endregion

    #region IConvertibleToString Members

    public string ToString()
    {
        // let's suppose that this is equivalent to the current implementation of Object.ToString
        return this.GetType().ToString();
    }

    #endregion

    // this one is perhaps a little goofy, since it doesn't satisfy any interface
    // In order to be equivalent to the current Object.MemberwiseClone implementation, I've made this protected,
    // but we cannot have a protected method that implements an interface, so this throws a wrench into things.
    protected MyClassExampleA MemberwiseClone()
    {
        // let's suppose that this is equivalent ot the current implementation of Object.MemberwiseClone
        return (MyClassExampleA)ClrInternals.MemberwiseClone(this);
    }

    // ** All of the above code is just a representation of the implicit semantics that the compiler/CLR applies to a class.  Perhaps this code is not actually generated by the compiler for each class (that would be a lot of duplication!), but rather the CLR might handle this logic internally
}


// Ok, so now I'm implementing a general Stack class
public class Stack
{
    // what type should I use for the parameter?
    // I have five different interfaces to choose from that I know all classes implement, but which one should I pick?
    public void Push(type??? item)
    {
        // ...
    }

    // what type should I use for the return type?
    // I have five interfaces to choose from, but if I return one,
    // then my caller can't utilize the methods defined in the other interfaces without casting.
    // I know all classes implement all five interfaces, but is it possible that my Stack might also contain non-class objects that don't implement all interfaces?  In that case it might be dangerous for the caller to cast the return value from one interface to another.
    public type??? Pop()
    {
        // ...
    }

    // In C++ I could have used void* or defined the Stack class as a template
}

// moving on...
class StackUtilizer
{
    // here I try to utilize the Stack class
    public void UseStack(Stack stack)
    {
        // what type should I use for the variable to hold the result of the Stack.Pop method?
        type??? item = stack.Pop();

        // if I use IEquatable
        IEquatable item1 = stack.Pop();

        IEquatable item2 = stack.Pop();

        item1.Equals(item2); // then I can do this

        Type itemType = item1.GetType(); // but I can't do this

        string s = item1.ToString(); // nor can I do this

        // Ok, this calls for another interface that composes all of these other interfaces into one
    }
}


// let's define a single interface that pulls all of these other interfaces together
public interface IObject : IEquatable, IHashCodeGenerator, ITypeIdentifiable, IConvertibleToString, IMemberwiseCloneable
{
    // no need to define any methods on this interface.  The purpose of this interface is merely to consolidate all of these other basic interfaces together.
}

// now we change the compiler rule to say that all classes implicitly implement the IObject interface
class MyClassExampleB : IObject
{
    // ... <refer to MyClassExampleA for the implicit implementation of the interfaces>
}

// now let's try implementing that Stack class again
public class Stack
{
    // I know that all classes implement the IObject interface, so it is an acceptable type to use as a parameter
    public void Push(IObject item)
    {
        // ...
    }

    // again, since all classes implement IObject, I can use it as the return type
    public IObject Pop()
    {
        // ...
        throw new NotImplementedException("This is an example.  The implementation of this method is irrelevant.");
    }
}

class StackUtilizer
{
    // here I try to utilize the Stack class
    public void UseStack(Stack stack)
    {
        // now I can just use IObject for my variables holding the return value of the Stack.Pop method
        IObject item = stack.Pop();

        // if I use IObject
        IObject item1 = stack.Pop();

        IObject item2 = stack.Pop();

        item1.Equals(item2); // then I can do this

        Type itemType = item1.GetType(); // and I can do this

        string s = item1.ToString(); // and I can do this
    }
}

所以,最后我们还是有一个 IObject 接口,类似于现在的 System.Object 类。悬而未决的问题是编译器/CLR 将如何处理执行我们的规则,即所有类都实现 IObject 接口。我可以想到三种可能的方法:

  1. 编译器为每个类生成隐式接口实现,这会导致大量重复。
  2. CLR 将以一种不需要编译器实际为每个类生成代码的特殊方式处理这些接口实现。
  3. 我们定义了一个基类,我们称它为Object(开始听起来很熟悉?),它实现了IObject接口,我们将规则更改为所有类都隐式继承自Object(这正是我们今天所拥有的,但没有接口)。
于 2010-09-08T21:26:24.887 回答
1

是的,对象类型可能会被滥用,但它为 .NET 世界提供了开创性的功能(最重要的是 IMO 是 GetType())。因此,如果有问题,则与对象的类型无关。

替代方案很多,包括泛型和 OOP 实践,例如接口......

于 2010-09-08T18:44:08.260 回答