ValueTypesはどのようにしてObject(ReferenceType)から派生し、それでもValueTypesのままですか?


83

C#では、構造体をクラスから派生させることはできませんが、すべてのValueTypeはObjectから派生します。この区別はどこで行われますか?

CLRはこれをどのように処理しますか?


System.ValueTypeCLR型システムにおけるタイプの黒魔術の結果。
RBT 2016

回答:


107

C#では、構造体をクラスから派生させることはできません

あなたの声明は間違っているので、あなたの混乱。C#では、構造体をクラスから派生させることできます。すべての構造体は、System.Objectから派生した同じクラスSystem.ValueTypeから派生します。そして、すべての列挙型はSystem.Enumから派生します。

更新:いくつかの(現在は削除されている)コメントに混乱があり、明確にする必要があります。私はいくつかの追加の質問をします:

構造体は基本型から派生していますか?

明らかにそうです。これは、仕様の最初のページを読むことで確認できます。

intやdoubleなどのプリミティブ型を含むすべてのC#型は、単一のルートオブジェクト型から継承します。

さて、私は仕様がここでのケースを誇張していることに注意します。ポインタタイプはオブジェクトから派生するものではなく、インターフェイスタイプとタイプパラメータタイプの派生関係は、このスケッチが示すよりも複雑です。ただし、明らかに、すべての構造体タイプが基本タイプから派生している場合があります。

構造体型が基本型から派生していることを私たちが知っている他の方法はありますか?

承知しました。構造体タイプはをオーバーライドできますToString。基本タイプの仮想メソッドではない場合、それは何をオーバーライドしますか?したがって、基本タイプが必要です。その基本型はクラスです。

選択したクラスからユーザー定義の構造体を派生させることはできますか?

明らかにありません。これは、構造体がクラスから派生していないことを意味するものではありません。構造体はクラスから派生し、それによってそのクラスの継承可能なメンバーを継承します。実際、構造体は特定のクラスから派生する必要があります。列挙型はから派生する必要がありEnum、構造体はから派生する必要がありますValueType。これらは必須であるため、C#言語では、派生関係をコードで記述すること禁じています。

なぜそれを禁止するのですか?

関係が必要な場合、言語設計者には、(1)ユーザーに必要な呪文を入力するように要求する、(2)オプションにする、または(3)禁止するというオプションがあります。それぞれに長所と短所があり、C#言語の設計者はそれぞれの特定の詳細に応じて異なる方法を選択しました。

たとえば、constフィールドは静的である必要がありますが、そうすることは最初に無意味な言い回しであり、2番目に非静的constフィールドがあることを意味するためであると言うことは禁じられています。ただし、開発者が選択の余地がない場合でも、オーバーロードされた演算子は静的としてマークする必要があります。開発者にとって、演算子のオーバーロードがインスタンスメソッドであると信じるのは簡単すぎます。これは、「静的」が「仮想」も可能性であることを意味するとユーザーが信じるようになるかもしれないという懸念を無効にします。

この場合、構造体がValueTypeから派生していることをユーザーに要求することは、単なる過剰な言い回しのように見え、構造体別のタイプから派生している可能性があることを意味します。これらの両方の問題を排除するために、C#では、構造体が基本型から派生していることをコードで記述することは違法ですが、明らかにそうです。

同様に、すべてのデリゲート型は、から派生しMulticastDelegateますが、C#はする必要がないことを言います。

これで、C#のすべての構造体がクラスから派生することがわかりました。

継承クラスからの派生の関係は何ですか?

多くの人がC#の継承関係に混乱しています。継承関係は非常に単純です。構造体、クラス、またはデリゲートタイプDがクラスタイプBから派生している場合、Bの継承可能なメンバーもDのメンバーです。それはそれと同じくらい簡単です。

構造体がValueTypeから派生していると言うとき、継承に関してはどういう意味ですか?単純に、ValueTypeのすべての継承可能なメンバーは構造体のメンバーでもあります。これはToString、たとえば、構造体がの実装を取得する方法です。構造体の基本クラスから継承されます。

すべての継承可能なメンバー?確かに違います。個人会員は遺伝性ですか?

はい。基本クラスのすべてのプライベートメンバーは、派生型のメンバーでもあります。もちろん、呼び出しサイトがメンバーのアクセシビリティドメインにない場合、それらのメンバーを名前で呼び出すことは違法です。メンバーがいるからといって、使えるとは限りません!

ここで、元の回答を続行します。


CLRはこれをどのように処理しますか?

とても良い。:-)

値型を値型にするのは、そのインスタンスがvalueによってコピーされることです。参照型を参照型にするのは、そのインスタンスが参照によってコピーされることです。値型と参照型の間の継承関係はどういうわけか特別で珍しいという信念を持っているようですが、私はその信念が何であるかを理解していません。継承は、物事がどのようにコピーされるかとは何の関係もありません。

このように見てください。私があなたに次の事実を話したとしましょう:

  • ボックスには、赤いボックスと青いボックスの2種類があります。

  • すべての赤いボックスは空です。

  • O、V、Eと呼ばれる3つの特別な青いボックスがあります。

  • Oはどの箱にも入っていません。

  • VはOの中にあります。

  • EはVの内側にあります。

  • Vの中に他の青いボックスはありません。

  • Eの中に青いボックスはありません。

  • すべての赤いボックスはVまたはEのいずれかにあります。

  • O以外のすべての青いボックスは、それ自体が青いボックスの中にあります。

青いボックスは参照型、赤いボックスは値型、OはSystem.Object、VはSystem.ValueType、EはSystem.Enum、「内部」関係は「派生元」です。

これは完全に一貫性のある単純なルールのセットであり、段ボールがたくさんあり、忍耐力があれば、自分で簡単に実装できます。ボックスが赤か青かは、中身とは関係ありません。現実の世界では、青いボックスの中に赤いボックスを入れることは完全に可能です。CLRでは、System.ValueTypeまたはSystem.Enumのいずれかである限り、参照型から継承する値型を作成することは完全に合法です。

それで、あなたの質問を言い換えましょう:

ValueTypesはどのようにしてObject(ReferenceType)から派生し、それでもValueTypesのままですか?

なので

すべての赤いボックス(値型)が、青いボックス(参照型)でありながら赤いボックス(値型)であるボックスO(System.Object)の内側(派生)にある可能性はありますか?

あなたがそのようにそれを表現するとき、私はそれが明白であることを望みます。青いボックスOの中にあるボックスVの中に赤いボックスを入れるのを妨げるものは何もありません。なぜあるのでしょうか?


追加の更新:

ジョアンの最初の質問は、それがどのように可能かについてでした値型が参照型から派生していること。私の最初の答えは、CLRが完全に異なる表現を持つ2つのものの間に派生関係があるという事実を説明するために使用するメカニズムのいずれも実際には説明していませんでした。つまり、参照されるデータにオブジェクトヘッダーがあるかどうかです。同期ブロック、ガベージコレクションの目的で独自のストレージを所有しているかどうかなど。これらのメカニズムは複雑であり、1つの答えで説明するには複雑すぎます。CLR型システムのルールは、C#で見られるやや単純化されたフレーバーよりもかなり複雑です。たとえば、型のボックス化されたバージョンとボックス化されていないバージョンの間に強い区別はありません。ジェネリックスの導入により、CLRにさらに複雑さが増しました。


8
言語構成は意味のあるものでなければなりません。任意の参照型から派生した任意の値型を持つとはどういう意味ですか?ユーザー定義の暗黙的な変換でも達成できないようなスキームで達成できることはありますか?
Eric Lippert

2
私はそうは思わない。グループとして表示される多くのvaluetypeで使用できるメンバーをいくつか用意できると思いました。これは、抽象クラスを使用して構造体を派生させることができます。暗黙の変換を使用できると思いますが、パフォーマンスが低下しますよね?あなたがそれらの何百万もしているなら。
Joan Venge

8
ああ、分かった。継承は、「一種の」関係をモデル化するためのメカニズムとしてではなく、単に関連する一連のタイプ間でコードを共有するためのメカニズムとして使用する必要があります。個人的には、コード共有の便宜のために継承を使用することは避けようとしていますが、これは合理的なシナリオのようです。
Eric Lippert

3
Joanが動作を一度定義すると、インターフェイスを作成し、動作を共有する構造体にインターフェイスを実装させてから、インターフェイスで動作する拡張メソッドを作成できます。このアプローチの潜在的な問題の1つは、インターフェイスメソッドを呼び出すときに、構造体が最初にボックス化され、コピーされたボックス化された値が拡張メソッドに渡されることです。状態の変化はオブジェクトのコピーで発生し、APIのユーザーには直感的でない可能性があります。
David Silva Smith

2
@Sipo:公平を期すために、質問には「CLRはこれをどのように処理するのですか?」が含まれています。そしてその答えは、CLRがこれらのルールをどのように実装するかをうまく説明しています。しかし、ここに問題があります。言語を実装するシステムには、その言語と同じルールがないことを期待する必要あります。実装システムは必然的に低レベルですが、その低レベルシステムのルールをその上に構築された高レベルシステムのルールと混同しないようにしましょう。確かに、CLR型システムは、私の答えで述べたように、ボックス化された値型とボックス化されていない値型を区別します。しかし、C#はそうではありません
エリックリペット

21

小さな修正ですが、C#では、構造体がクラスだけでなく、何かからカスタム派生することはできません。構造体が実行できるのは、派生とは大きく異なるインターフェースを実装することだけです。

これに答える最良の方法はそれValueTypeが特別だと思います。これは基本的に、CLR型システムのすべての値型の基本クラスです。「CLRがこれをどのように処理するか」に答える方法を知るのは難しいです。なぜなら、それは単にCLRのルールだからです。


[System.ValueTypeから暗黙的に派生する場合を除いて]何からも派生しない構造体についての良い点は+1です。
リードコプシー

2
それValueTypeは特別だとあなたは言いますが、ValueTypeそれ自体が実際には参照型であることを明示的に言及する価値があります。
LukeH 2009年

内部的に構造体がクラスから派生する可能性がある場合、なぜそれらはそれをすべての人に公開しないのですか?
Joan Venge

1
@ジョアン:彼らは本当にそうではありません。これは、構造体をオブジェクトにキャストできるようにするためであり、ユーティリティのためにあります。ただし、技術的には、クラスの実装方法と比較すると、値型はCLRによってまったく異なる方法で処理されます。
リードコプシー

2
@JoanVengeここでの混乱は、構造体がCLR内のValueTypeクラスから派生していると言っていると思います。CLR内では構造体は実際には存在せず、CLR内の「構造体」の実装は実際にはValueTypeクラスであると言った方が正しいと思います。したがって、構造体がCLRのValueTypeから継承しているわけではありません。
wired_in

20

これは、すべての型をSystem.Objectとして扱うことができるようにするために、CLRによって維持されるやや人工的な構造です。

値の型は、System.ObjectからSystem.ValueTypeを介して派生します。ここで、特別な処理が行われます(つまり、CLRは、ValueTypeから派生するすべての型のボックス化/ボックス化解除などを処理します)。


6

あなたの声明は間違っているので、あなたの混乱。C#では、構造体をクラスから派生させることができます。すべての構造体は、同じクラスSystem.ValueTypeから派生します

それでは、これを試してみましょう:

 struct MyStruct :  System.ValueType
 {
 }

これはコンパイルすらしません。コンパイラは、「インターフェイスリストのタイプ 'System.ValueType'はインターフェイスではありません」と通知します。

構造体であるInt32を逆コンパイルすると、次のことがわかります。

public struct Int32:IComparable、IFormattable、IConvertible {}、 System.ValueTypeから派生していることは言うまでもありません。しかし、オブジェクトブラウザでは、Int32がSystem.ValueTypeから継承していることがわかります。

したがって、これらすべてが私を信じさせます:

これに答える最良の方法は、ValueTypeが特別であるということだと思います。これは基本的に、CLR型システムのすべての値型の基本クラスです。「CLRがこれをどのように処理するか」に答える方法を知るのは難しいです。なぜなら、それは単にCLRのルールだからです。


.NETでも同じデータ構造を使用して、値型と参照型の内容を記述しますが、CLRは、から派生したものとして定義された型定義を検出するとValueType、それを使用して2種類のオブジェクトを定義します。参照型、および型継承システムの外部にある格納場所型のように動作します。これらの2種類のものは相互に排他的なコンテキストで使用されるため、同じタイプ記述子を両方に使用できます。CLRレベルでは、構造体は親がSystem.ValueTypeであるクラスとして定義されますが、C#...
supercat 2016年

...構造体が(System.ValueType)から継承できるものが1つしかないため、構造体が何かから継承することを指定することを禁止System.ValueTypeし、そのように宣言されたクラスは値型のように動作するため、クラスが継承元を指定することを禁止します。
スーパーキャット2016年

3

ボックス化された値型は事実上参照型です(1つのように歩き、1つのようにいんちきするので、事実上1つです)。ValueTypeは実際には値型の基本型ではなく、Object型にキャストするときに値型を変換できる基本参照型であることをお勧めします。ボックス化されていない値型自体は、オブジェクト階層の外側にあります。


1
私は「ValueTypeには本当にの基本型ではない、あなたが意味だと思いタイプ」
wired_in

1
@wired_in:ありがとう。修正しました。
スーパーキャット2016年

2

理論的根拠

すべての答えの中で、@ supercatの答えは実際の答えに最も近いものです。他の答えは実際には質問に答えておらず、まったく間違った主張をしているので(たとえば、値型は何かから継承している)、私は質問に答えることにしました。

 

プロローグ

この回答は、私自身のリバースエンジニアリングとCLI仕様に基づいています。

structおよびclassはC#キーワードです。CLIに関する限り、すべてのタイプ(クラス、インターフェース、構造体など)はクラス定義によって定義されます。

たとえば、オブジェクトタイプ(C#では「として知られているclass)」は次のように定義されます。

.class MyClass
{
}

 

インターフェイスは、interfaceセマンティック属性を持つクラス定義によって定義されます。

.class interface MyInterface
{
}

 

値型はどうですか?

構造体がSystem.ValueType値型から継承でき、それでも値型であることができる理由は、そうではないからです。

値型は単純なデータ構造です。値型がないではないから継承何でも、彼らはできませんインターフェースを実装します。値型はどの型のサブ型でもありません。また、型情報もありません。値型のメモリアドレスを指定すると、非表示フィールドに型情報を持つ参照型とは異なり、値型が何を表すかを識別することはできません。

次のC#構造体を想像すると:

namespace MyNamespace
{
    struct MyValueType : ICloneable
    {
        public int A;
        public int B;
        public int C;

        public object Clone()
        {
            // body omitted
        }
    }
}

以下は、その構造体のILクラス定義です。

.class MyNamespace.MyValueType extends [mscorlib]System.ValueType implements [mscorlib]System.ICloneable
{
    .field public int32 A;
    .field public int32 B;
    .field public int32 C;

    .method public final hidebysig newslot virtual instance object Clone() cil managed
    {
        // body omitted
    }
}

では、ここで何が起こっているのでしょうか。これは明らかに延びているSystem.ValueTypeオブジェクト/参照型であり、これは、器具System.ICloneable

説明は、クラス定義が拡張されるSystem.ValueTypeと、実際には2つのものを定義するということです。値型と、値型の対応するボックス型です。クラス定義のメンバーは、値型と対応するボックス型の両方の表現を定義します。拡張して実装するのは値型ではなく、対応するボックス型です。extendsそしてimplements、キーワードだけ箱入りタイプに適用されます。

明確にするために、上記のクラス定義は2つのことを行います。

  1. 3つのフィールド(および1つのメソッド)を持つ値型を定義します。それは何からも継承せず、インターフェースを実装しません(値型はどちらも実行できません)。
  2. 3つのフィールド(および1つのインターフェイスメソッドの実装)を使用してオブジェクトタイプ(ボックス化されたタイプ)を定義し、から継承しSystem.ValueTypeSystem.ICloneableインターフェイスを実装します。

また、キーワードが指定されているSystem.ValueTypeかどうかに関係sealedなく、拡張するクラス定義も本質的に封印されていることに注意してください。

値型は単なる単純な構造であり、継承、実装、およびポリモーフィズムのサポートを行わないため、他の型システムでは使用できません。これを回避するために、CLRは、値型に加えて、ボックス型と呼ばれる同じフィールドを持つ対応する参照型も定義します。したがって、値型をメソッドに渡すことはできませんがobject、対応するボックス化された型渡すことができます。

 

さて、C#でメソッドを定義するとしたら

public static void BlaBla(MyNamespace.MyValueType x)

メソッドが値型を取ることを知っていますMyNamespace.MyValueType

上記で、structC#のキーワードから生じるクラス定義は、実際には値型とオブジェクト型の両方を定義することを学びました。ただし、参照できるのは定義された値型のみです。CLI仕様では、constraintキーワードboxedを使用して型のボックス化されたバージョンを参照できると記載されていますが、このキーワードは存在しません(ECMA-335、II.13.1値型の参照を参照)。しかし、それが少しの間そうなると想像してみましょう。

ILの型を参照する場合、いくつかの制約がサポートされています。その中にはとがclassありvaluetypeます。を使用する場合はvaluetype MyNamespace.MyType、MyNamespace.MyTypeという値型クラス定義を指定しています。同様に、class MyNamespace.MyTypeMyNamespace.MyTypeというオブジェクトタイプクラス定義を指定するために使用できます。つまり、ILでは、同じ名前の値型(struct)とオブジェクト型(class)を持ち、それらを区別することができます。これでboxed、CLI仕様で指定されたキーワードが実際に実装された場合、boxed MyNamespace.MyTypeMyNamespace.MyTypeと呼ばれる値型クラス定義のボックス化された型を指定するために使用できます。

したがって、.method static void Print(valuetype MyNamespace.MyType test) cil managedという名前の値型クラス定義によって定義された値型を取りますMyNamespace.MyType

while.method static void Print(class MyNamespace.MyType test) cil managedは、という名前のオブジェクト型クラス定義によって定義されたオブジェクト型を取りますMyNamespace.MyType

同様に、boxedがキーワードの場合、.method static void Print(boxed MyNamespace.MyType test) cil managedという名前のクラス定義によって定義された値型のボックス化された型を取りMyNamespace.MyTypeます。

次に、ボックス化された型を他のオブジェクト型と同じようにインスタンス化し、それをSystem.ValueTypeobjectまたはboxed MyNamespace.MyValueType引数として受け取る任意のメソッドに渡すことができます。これは、すべての目的と目的で、他の参照型と同じように機能します。これは値型ではありませんが、値型の対応するボックス型です。

 

概要

それで、要約すると、そして質問に答えるために:

値の種類はされていない型を参照してくださいませんから継承するSystem.ValueTypeか、他のタイプ、および彼らがすることができないのインターフェイスを実装します。対応箱入りされているタイプ定義されてはないから継承System.ValueTypeし、できるインタフェースを実装します。

.class定義は、状況に応じて異なるものを定義します。

  • 場合はinterface意味属性が指定されている、クラス定義は、インターフェイスを定義します。
  • 場合interfaceセマンティック属性が指定されていない、と定義が拡張していないSystem.ValueType、クラス定義は、オブジェクト型(クラス)を定義します。
  • 場合interfaceセマンティック属性が指定されていない、と定義がない延びるSystem.ValueType、クラス定義は、値型定義それに対応するボックス化型(構造体)を。

メモリレイアウト

このセクションでは、32ビットプロセスを想定しています

すでに述べたように、値型には型情報がないため、値型が何を表しているのかをメモリ位置から特定することはできません。構造体は単純なデータ型を記述し、それが定義するフィールドのみを含みます。

public struct MyStruct
{
    public int A;
    public short B;
    public int C;
}

MyStructのインスタンスがアドレス0x1000に割り当てられたと想像すると、これはメモリレイアウトです。

0x1000: int A;
0x1004: short B;
0x1006: 2 byte padding
0x1008: int C;

構造体のデフォルトはシーケンシャルレイアウトです。フィールドは、独自のサイズの境界に配置されます。これを満たすためにパディングが追加されます。

 

まったく同じ方法でクラスを定義すると、次のようになります。

public class MyClass
{
    public int A;
    public short B;
    public int C;
}

同じアドレスを想像すると、メモリレイアウトは次のようになります。

0x1000: Pointer to object header
0x1004: int A;
0x1008: int C;
0x100C: short B;
0x100E: 2 byte padding
0x1010: 4 bytes extra

クラスはデフォルトで自動レイアウトに設定され、JITコンパイラはそれらを最適な順序で配置します。フィールドは、独自のサイズの境界に配置されます。これを満たすためにパディングが追加されます。理由はわかりませんが、すべてのクラスの最後に常に4バイトが追加されています。

オフセット0には、タイプ情報、仮想メソッドテーブルなどを含むオブジェクトヘッダーのアドレスが含まれます。これにより、ランタイムは、値タイプとは異なり、アドレスのデータが何を表すかを識別できます。

したがって、値型は継承、インターフェース、またはポリモーフィズムをサポートしていません。

メソッド

値型には仮想メソッドテーブルがないため、ポリモーフィズムはサポートされていません。 ただし、対応するボックス型

構造体のインスタンスがあり、でToString()定義されているような仮想メソッドを呼び出そうとするとSystem.Object、ランタイムは構造体をボックス化する必要があります。

MyStruct myStruct = new MyStruct();
Console.WriteLine(myStruct.ToString()); // ToString() call causes boxing of MyStruct.

ただし、構造体がオーバーライドToString()されると、呼び出しは静的にバインドされ、ランタイムはMyStruct.ToString()ボックス化せずに、仮想メソッドテーブルを調べずに呼び出します(構造体には何もありません)。このため、ToString()通話をインライン化することもできます。

構造体がオーバーライドさToString()れてボックス化されている場合、呼び出しは仮想メソッドテーブルを使用して解決されます。

System.ValueType myStruct = new MyStruct(); // Creates a new instance of the boxed type of MyStruct.
Console.WriteLine(myStruct.ToString()); // ToString() is now called through the virtual method table.

ただし、ToString()これは構造体で定義されているため、構造体の値を操作するため、値型が必要です。ボックス化されたタイプには、他のクラスと同様に、オブジェクトヘッダーがあります。ToString()構造体で定義されたメソッドがthisポインタのボックス型で直接呼び出された場合、のフィールドAにアクセスしようとするとMyStruct、オフセット0にアクセスします。これは、ボックス型ではオブジェクトヘッダーポインタになります。したがって、ボックス型には、の実際のオーバーライドを行う隠しメソッドがありますToString()。この非表示のメソッドunboxは、ボックス化された型のボックス化を解除し(アドレス計算のみ、IL命令のように)ToString()、構造体で定義されたものを静的に呼び出します。

同様に、ボックス化されたタイプには、実装されたインターフェイスメソッドごとに非表示のメソッドがあり、同じボックス化解除を実行してから、構造体で定義されたメソッドを静的に呼び出します。

 

CLI仕様

ボクシング

I.8.2.4すべての値型について、CTSはボックス型と呼ばれる対応する参照型を定義します。逆は当てはまりません。一般に、参照型には対応する値型がありません。ボックス型の値(ボックス型の値)の表現は、値型の値を格納できる場所です。ボックス化されたタイプはオブジェクトタイプであり、ボックス化された値はオブジェクトです。

値型の定義

I.8.9.7クラス定義によって定義されたすべてのタイプがオブジェクトタイプであるとは限りません(§I.8.2.3を参照)。特に、値型はオブジェクト型ではありませんが、クラス定義を使用して定義されます。値型のクラス定義は、(ボックス化されていない)値型と関連するボックス化された型の両方を定義します(§I.8.2.4を参照)。クラス定義のメンバーは、両方の表現を定義します。

II.10.1.3タイプセマンティック属性は、インターフェイス、クラス、または値タイプを定義するかどうかを指定します。interface属性は、インターフェイスを指定します。この属性が存在せず、定義が(直接的または間接的に)System.ValueTypeを拡張し、定義がSystem.Enum用でない場合、値型を定義する必要があります(§II.13)。それ以外の場合は、クラスを定義する必要があります(§II.11)。

値型は継承しません

I.8.9.10ボックス化されていないフォームでは、値型はどの型からも継承されません。ボックス化された値型は、列挙型でない限り、System.ValueTypeから直接継承します。列挙型の場合は、System.Enumから継承します。箱入りの値型は封印されなければならない。

II.13ボックス化されていない値タイプは、別のタイプのサブタイプとは見なされず、ボックス化されていない値タイプでisinst命令(パーティションIIIを参照)を使用することは無効です。ただし、isinst命令は、ボックス化された値型に使用できます。

I.8.9.10値型は継承しません。むしろ、クラス定義で指定された基本型は、ボックス化された型の基本型を定義します。

値型はインターフェースを実装しません

I.8.9.7値型はインターフェイスコントラクトをサポートしていませんが、関連するボックス型はサポートしています。

II.13値型は、0個以上のインターフェースを実装する必要がありますが、これはボックス形式(§II.13.3)でのみ意味があります。

I.8.2.4インターフェースと継承は、参照型でのみ定義されます。したがって、値型定義(§I.8.9.7)は、値型とそれが継承するクラス(System.ValueTypeまたはSystem.Enum)によって実装されるインターフェイスの両方を指定できますが、これらはボックス化された値にのみ適用されます。 。

存在しないボックス化されたキーワード

II.13.1値型のボックス化されていない形式は、valuetypeキーワードの後に​​型参照を使用して参照されるものとします。値型のボックス化された形式は、ボックス化されたキーワードの後に​​型参照を使用して参照されます。

注:ここでは仕様が間違っていますboxed。キーワードはありません。

エピローグ

値型がどのように継承されるかについての混乱の一部は、C#がキャスト構文を使用してボックス化とボックス化解除を実行するという事実に起因すると思います。これにより、キャストを実行しているように見えますが、実際にはそうではありません(ただし、間違ったタイプをボックス化解除しようとすると、CLRはInvalidCastExceptionをスローします)。 (object)myStructC#では、値型のボックス化された型の新しいインスタンスを作成します。キャストは実行されません。同様に(MyStruct)obj、C#では、ボックス化された型のボックスを解除し、値の部分をコピーします。キャストは実行されません。


1
最後に、それがどのように機能するかを明確に説明する答え!これは受け入れられた答えに値する。よくやった!
ジャストシャドウ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.