7

オブジェクトを使用すると、「特定のタイプがないのでオブジェクトを使用する」など、特定の問題を解決する簡単な方法のように思われるため、これを尋ねています。

また、これに興味を持った理由は、同僚が、.NET が真のオブジェクト指向プラットフォームである場合、Object のようなすべての型をキャッチする必要はないだろうと私に言ったからです。

.NET に Object 型がない場合、発生する問題を解決して同じように機能させるには、どのような方法があるでしょうか?

また、仕事で毎日使用しているため、これは.NETをbashするものではないことに注意してください。それについてもっと知りたいだけです。

編集: 私が覚えていたもう 1 つの注意は、オブジェクト型が存在するためです。その効果は .NET 全体に波及します。同様に IEnumerable が存在しますが、IEnumerable<T> もあります。また、多くの状況で、ジェネリック バージョンと非ジェネリック バージョンの両方を実装する必要があります。

4

9 に答える 9

3

あなたの友人はおそらく動的言語 (ruby や python など) を使っていますよね?

強く型付けされた言語は「オブジェクト指向」ではなく「クラス指向」と呼ぶべきだと考える人は非常に多い。すべての可能な動作とポリモーフィズムはクラス定義で前もって行う必要があるからである。メソッドが何かを受け入れる場合、クラスではなくオブジェクトの機能に基づいてチェックが行われます。これは、よりオブジェクト指向のアプローチであると言えます。

私はこの議論にちょっと矛盾しています。プログラミング界には、問題や要件が何であれ、OO は明確に良いものであるという骨の折れる信念があります。そのため、人々は「あれこれはオブジェクト指向ではない」と言って議論に勝とうとする傾向があり、オブジェクト指向は善の同義語であるため、彼らが勝ちます。静的言語を扱うのは面倒だと思いますが、静的言語を OO ではないと呼ぶのは、あなたの主張を不誠実に伝える方法だと思います。一方で、プログラマーが自分のコンフォート ゾーンの外に出て、何かを行うための新しい方法を学ばせる (他に理由がなければ、議論に勝つための) ものは、完全に悪いものではありません。私が言ったように、矛盾しています。:)

于 2010-09-08T18:56:57.163 に答える
3

解決される問題は、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

私はこのテーマについて特に詳しいわけではありませんが、私の観点からは、コンポーネントがどのように消費されるかを事前に知らなくても、人々がポリモーフィック コンポーネントを構築できるようにすることは有益でした。それはどういう意味ですか?説明してみましょう。

ArrayList.NET フレームワークのクラスを使った簡単な例を見てみましょう。これは、ジェネリックが導入される前の元のフレームワークの一部でした。クラスの作成者は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];
            // ...
        }

これが独自のアプリケーションで、DoctorLawyer、およびPetDetectiveクラスがすべて共通の基本クラスから派生していることがわかっている場合、理論上は、クラスではなくクラスPersonに基づいて独自のリンク リスト実装を構築できます。ただし、ビルドおよびテスト済みのクラスが既にある場合、これは多くの余分な作業であり、メリットはほとんどありません。基本クラスに固有のものにしたい場合は、派生オブジェクトのみを受け入れるラッパークラスをいつでも作成できます。PersonObjectArrayListPersonArrayListPerson

C++ では、「void ポインター」データ型 ( ) を使用して、基本的に同じことを行うことができますvoid*。ただし、C++ はテンプレート (ジェネリックと非常によく似ています) もサポートしていたため、他のどのクラスと一緒に使用されるかを詳細に知らなくても、便利なコンポーネントを簡単に作成できました。C# は最初はジェネリックをサポートしていなかったため、Object型を使用することが、他の人が使用できる一般的なポリモーフィック コンポーネントを作成する唯一の方法でした。

于 2010-09-08T19:01:59.083 に答える
1

はい、オブジェクト型は誤用される可能性がありますが、.NET の世界に重要な機能を提供します (とりわけ、IMO は GetType() です)。したがって、問題がある場合、それはオブジェクトの型を持つことではありません。

ジェネリックやインターフェイスなどの OOP プラクティスなど、多くの代替手段があります...

于 2010-09-08T18:44:08.260 に答える
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
    }
}

したがって、最終的には、現在の System.Object クラスに似た IObject インターフェイスが残っています。未解決の問題は、コンパイラ/CLR が、すべてのクラスが IObject インターフェイスを実装するというルールを適用する方法です。次の 3 つのアプローチが考えられます。

  1. コンパイラは各クラスの暗黙的なインターフェイスの実装を生成しますが、これにより多くの重複が発生します。
  2. CLR は、コンパイラが各クラスのコードを実際に生成する必要のない特別な方法で、これらのインターフェイスの実装を処理します。
  3. インターフェイスを実装する基本クラスを定義しますObject(おなじみのように聞こえますか?)。IObjectルールを変更して、すべてのクラスが暗黙的に継承するようにしますObject(これはまさに現在のものですが、インターフェイスはありません)。
于 2010-09-08T21:26:24.887 に答える
1

オブジェクト派生階層に 1 つの統一された頭部がなければ、動的型付けに頼らずに任意の 2 つのものの間で等しいかどうかをテストすることは不可能です。

それ以外では、オブジェクトの機能は、別のインターフェイス (IEqualable と IConvertableToString) でほぼ同様に処理できたのではないかと思います。一方、オブジェクトの仮想メソッドは非常に便利な場合があり、特に ToString は、プログラムの状態を表示するときに IDE またはデバッガーで使用できます。実に実用的なデザインです。

于 2010-09-08T18:50:26.593 に答える