web-dev-qa-db-ja.com

C#はVB.NETのDirectCastと同等ですか?

C#にはVB.NETのDirectCastと同等のものがありますか?

()キャストと 'as'キーワードがありますが、CTypeとTryCastに対応していることは承知しています。

明確にするために、これらのキーワードは次のことを行います。

CType /()casts:既に正しい型である場合はキャストします。そうでない場合は、型コンバーターを探して呼び出します。型コンバーターが見つからない場合は、InvalidCastExceptionをスローします。

TryCast/"as"キーワード:正しい型であればキャストし、そうでなければnullを返します。

DirectCast:正しい型の場合はキャストし、そうでない場合はInvalidCastExceptionをスローします。

上記を詳しく説明した後、一部の人々はまだ()は同等であると答えているので、なぜそうでないのかをさらに詳しく説明します。

DirectCastでは、継承ツリーでの変換の縮小または拡大のみが許可されます。 ()のような異なるブランチ間での変換はサポートしていません。

C#-これはコンパイルして実行します:

//This code uses a type converter to go across an inheritance tree
double d = 10;
int i = (int)d;

VB.NET-これはコンパイルしません

'Direct cast can only go up or down a branch, never across to a different one.
Dim d As Double = 10
Dim i As Integer = DirectCast(d, Integer)

VB.NETで私のC#コードに相当するものはCTypeです。

'This compiles and runs
Dim d As Double = 10
Dim i As Integer = CType(d, Integer)
49
csauve

必要な機能がC#にないことは明らかです。これを試してみてください...

static T DirectCast<T>(object o, Type type) where T : class
{
    if (!(type.IsInstanceOfType(o)))
    {
        throw new ArgumentException();
    }
    T value = o as T;
    if (value == null && o != null)
    {
        throw new InvalidCastException();
    }
    return value;
}

または、VBとは異なりますが、次のように呼び出します。

static T DirectCast<T>(object o) where T : class
{
    T value = o as T;
    if (value == null && o != null)
    {
        throw new InvalidCastException();
    }
    return value;
}
13
Greg Bogumil

2回目の更新

OK、これは基本的にVB.NETでDirectCastが行うことを基本的に行うと提案されているC#メソッドです。

static T DirectCast<T>(object o) where T : class
{
    T value = o as T;
    if (value == null && o != null)
    {
        throw new InvalidCastException();
    }
    return value;
}

上記の方法の問題は次のとおりです:

  1. where T : class制約があり、DirectCastにはありません。
  2. 引数をSystem.Objectとして囲みます-繰り返しますが、DirectCastには当てはまりません(少なくとも私が知っていることではありません)。
  3. asを不必要に使用します(そのため、最初にclass制約があります)。 (T)oを呼び出すと、機能しない場合はInvalidCastExceptionがスローされます。 asを使用して値が一致するかどうかを確認し、最初に(T)oルートを行った場合にスローされるのと同じ例外をスローするだけですか?

メソッドは、次のようにDirectCastと同じ結果を提供するように実際に書き換えることができます。

static T DirectCast<T>(object o) {
    return (T)o;
}

おもしろい観察:このメソッドが実際に行っているのは、値をボックス化してから、そのボックス化を解除することです。言い換えれば、DirectCast<int>(12.0)は実際には(int)(object)12.0と同じです(そしてどちらも例外をスローします)。これを実現すると、提案されているDirectCast<T>メソッドはまったく不要になります。

ここで、DirectCast()を使用したキャストがVB.NETとC#でどのように「異なる」かの例を示します。

VB:

Dim i As Integer = 12
Dim l As Long = DirectCast(i, Long) ' does not compile '

C#:

int i = 12;
long l = i; // DOES compile

よし、一方はコンパイルし、もう一方はコンパイルしません。しかし、そのコードを見てください。 オブジェクトの型を既に知っている場合のDirectCastのポイントは何ですか?これは現実的な比較ではありません。VB.NETでは、上記のコードのようにDirectCastを呼び出す理由はないからです。 (VB.NETで値_タイプSystem.Int32であることがわかっている値をタイプSystem.Int64の値に変換する場合は、CLngではなくDirectCastを使用します。)そこにSystem.Objectと入力された変数がある場合、 thenDirectCastを使用することは理にかなっており、以下のコードは実際に同等です:

VB:

Dim i As Integer = 12
Dim o As Object = i
Dim l As Long = DirectCast(o, Long) ' compiles, throws an exception '

C#:

int i = 12;
object o = i;
long l = (long)o; // compiles, throws an exception

したがって、VB.NETでは、DirectCastを、実際に使用することが理にかなっているシナリオ(つまり、コンパイル時にオブジェクトの型がわからない場合)で、まっすぐな()と同じ-style cast in C#


[〜#〜] edit [〜#〜]:さて、いくつかのVBコンパイルできなかったコードを投稿したことを恥ずかしく思います。 、私はsecondanswerを撤回しますが、最初を維持します。

DirectCastの使用法を参照している場合、不明な型のオブジェクトを取得して目的の型にキャストしようとすると、isとC#の()キャストと同じです:

VB:

Dim o As Object = SomeObject()
Dim i As Integer = DirectCast(o, Integer)

C#:

object o = SomeObject();
int i = (int)o;

これは、oSystem.Objectとして入力された場合、C#の()操作がボックス化を解除しようとするためです。タイプが正確に一致しない場合、これは失敗します。たとえば、oがボックス化されたSystem.Doubleである場合、omust(int)oに変換される前にSystem.Doubleとしてアンボックス化されるため、System.Int32は例外をスローします(信じられない場合は試してください自分のために!)。


注:以下は、DirectCastnot拡大変換を実行するため不正確です;いずれにしても、私はそれを後世に残しています。

一方、拡大変換と縮小変換を扱う場合、C#で()操作を使用すると、先ほど指摘したように単純なキャストよりも多くの作業が行われます(つまり、(int)someDoubleを実行できます)。このシナリオでは、DirectCastはC#の単純な古い割り当てと同等です。

VB:

Dim i As Integer = 12
Dim l As Long = DirectCast(i, Long) ' does not compile, actually '

C#:

int i = 12;
long l = i;
8
Dan Tao

実際、コンパイラは、型付き変数を他の型に変換できないと推測した場合、DirectCast違反をキャッチします。

これらは実際の同等物です:

double d = 10;
int i = (int)d;

Dim d As Double = 10
Dim i As Integer = d

この構造の危険性に注意してください。VB.NETで単にdoubleを整数に割り当てるだけでは、誤ってdoubleが整数に縮小されます。

一方、C#プログラマーは、変数.NETを誤ってダウンサイズしないというコンパイル時の安全性を享受します。 VB.NETプログラマーは、常にDirectCastを安全なプログラミング習慣として置くことに取り組まなければなりません

これらは実際の同等物です:

// will not compile, cannot convert double to int

double d = 10;
int i = d; 

' will not compile, cannot convert double to int

Dim d As Double = 10
Dim i As Integer = DirectCast(d, Integer) 

[編集]

@ダンタオ:

C#でDirectCastを使用する必要はありません。ランタイムは、longから整数への値の読み込みも防ぎます。これはcsauveが競合していることです。C#にはDirectCastがありません。DirectCastはさまざまなタイプの変数の割り当てを防ぐことができますが、C#にはこのDirectCastがないためしかし、ご覧のとおり、C#のキャストはexactly DirectCastと同じです。これにより、InvalidCastExceptionランタイムエラーが発生します。

long l = 10;
object o = l;
int i = (int)o;

これにより、上記と同じランタイムエラーも発生します。

Dim l As Long = 10
Dim o As Object = l
Dim i As Integer = DirectCast(o, Integer)

さて、ここが「楽しい」部分の出番です。VB.NETでは、何かを達成するために多くのキーワードを覚えておく必要があります。 C#では、特定のキーワードが別のシナリオ(この変数のダウンキャストなど)で使用できる場合、それを実現するためだけに別のキーワードを発明しません。

C#では、これを行うだけです。

long l = 10;
object o = l;
int i = (int)(long)o;

VB.NETでは、変数を本当にダウンキャストしたい場合、それを行うための直交的な方法が必要な場合、つまり1つのキーワードを覚えているだけであれば、これを行う必要があります:

 Dim l As Long = 10
 Dim o As Object = l
 Dim i As Integer = DirectCast(DirectCast(o, Long), Integer)

しかし、それはコンパイルされませんので、整数へのロングキャストをどのように達成するのですか? VB.NETの他のキーワードを覚えておく必要があります。 C#では直交ですが、この構造を使用して変数のボックス化を解除します(typehere)、同じコンストラクトを使用してダウンキャスト/アップキャストします(typehere)。 VB.NETでは、オブジェクトから値をロードすることとダウンキャストすることとの間に根本的な切断があります。 VB.NETでは、これを行う必要があります。

 Dim l As Long = 10
 Dim o As Object = l
 Dim i As Integer = CType(o, Integer)

うーん。csauveの混乱は、C#が(typehere)、最初にダウンキャストに使用されます。第二に、同じコンストラクト(この投稿の最初の部分、object o = l)は、オブジェクトからの値のボックス化解除にも使用されます。DirectCastの安全な型変換動作が保証されているため、これらは同じです。

このダウンキャスト...

long l = 1;
int i = (int) l;

...は次のものと同等ではありません:

Dim l As Long = 1
Dim i As Integer = DirectCast(l, Integer)

ダウンキャスティングを実行する場合、これを行う必要があります。

Dim l As Long = 1
Dim i As Integer = CInt(l) ' can also use CType

VB.NETプログラマーが意図的にプログラミングし、コーディング中に眠くない場合、異なるタイプを割り当てることができないことを完全に認識しているときにDirectCastを使用するのはなぜですか? VB.NETプログラマーが本当に望んでいたものがダウンキャストである場合、そもそもDirectCastを試してはいけません。 VB.NETプログラマーは、DirectCastをダウンキャストに使用できないことを発見すると、作成したものをバックスペースし、CInt(またはCType)に置き換える必要があります。

1
Michael Buen

自分で実装できます:

static T CastTo<T>(this object obj) { return (T)obj; }

次のように使用できます。

3.5.CastTo<int>(); //throws InvalidCastException.

ジェネリックは実行時に「解決」されるが、型変換はコンパイル時に解決されるため、これは機能し、ユーザー定義のコンバーターは関与しません。フレームワークは、各Tに対して個別の実装を実際に生成しませんむしろ同様のTの実装を共有しているため、ランタイムにはカスタム変換を解決するための情報がありません。

1
Eamon Nerbonne

VB.NET:

Dim xxx as label = Directcast(sender, label)

C#:

label xxx = (label)sender;

DirectCastおよび()は常に同じILを生成するとは限らないため、これはVBとC#コンパイラの違いだと思います。

AFAICT、参照型はcastclass IL命令を使用してキャストされますが、値型の場合、コンパイラは入力型に応じて関連するILを生成します。

C#では、doubleからintegerにキャストするとconv.i4 IL命令。これは、値が大きすぎる場合に出力の符号ビットなどを陽気に上書きします。 VBでは、コンパイルエラーです。

興味深いことに、中間のobject変数を使用してdoubleを保持すると、C#とVBの両方でキャストが失敗しますが、実行時に失敗します。両方のコンパイラーは、変換を試みる代わりにunbox命令を発行します。

0
SSS

このシナリオは、DirectCastが非オブジェクト(オブジェクトキーワード)型のコンパイル時型チェックセキュリティについて誤った感覚を持ち、単にバックスペースにすることを意図している理由を最もよく表していると思います。

_float f = 10;
long l = f;

Option Strict On    
Dim f As Single = 10
Dim l As Long = f
_

C#コーダーは、floatがlongに直接割り当てられず、コンパイルされないことを発見すると、これを行います。

_long l = (long)f;
_

どちらが正しい。

さて、VB.NETコーダーに目を向けてみましょう。floatはlongに代入可能ではなく、コンパイルもできないことがわかったら、これを試みます。

_Dim l As Long = DirectCast(f, Long)
_

数秒後...

VB.Netプログラマー:「入札をさせてください。コンパイルしてください... !!!」

Googling-fuとMSDN-browsingのしばらく後:

VB.NETプログラマー:「ああ..だから、変数をキャストするためにこのCLngまたはCTypeコンストラクトを使わなければならない」

_Dim l As Long = CLng(f)
_

これが、DirectCastがコンパイル時の型チェックセキュリティについて誤った感覚を持っているということです。 DirectCastは、プログラマがいつ、どこで使用する必要があるかわからない場合に、バックスペースにすることを目的としています。 DirectCastはセキュリティブランケットであり、常時着用されていません

結局使用されない場合、このシナリオでDirectCastはどれほど便利ですか?


[編集]

@Jules

すべてのVB.NETプログラマーがDirectCastの実際の使用方法を知らないというつもりはありません。DirectCastがオブジェクト型(およびオブジェクト内にボックス化されたプリミティブ型)にのみ使用されることを意図していることもあります。 。

既存のC#コードをVB.NETに再コーディングするVB.NETコーダーが間違った結論に到達するシナリオの1つは、予想される(正しいかどうかにかかわらず)言語の対称性です。

彼がコードでこの構造を見ると...

_TextBox txt = (TextBox)sender;
_

...彼はそれをこれに翻訳します:

_Dim txt As TextBox = DirectCast(sender, TextBox)
_

どちらが正しい。

今、私たちプログラマーは対称性を愛しているので、私たちの中には(CLngを知らなければ私もそうかもしれません)このコードを変換する傾向があります...

_/* numbers are stored in file as float(component's file structure 
is designed by 3rd party company) */
float f = file.ReadFloat(0); 
long l = (long)f; // but we don't care about using the fractional part
_

...これに:

_Dim f As Single = file.ReadFloat(0)
Dim l As Long = DirectCast(f, Long)
_

C#の人がC#コードをVB.NETに変換する人である場合、彼はここで明らかに対称性の欠如に不満を感じるでしょう。

しかし、C#コードをVB.NETに変換するVB.NET担当者にとっては、C#コンパイラーは互換性のない型の割り当てをキャッチしないが、VB.NETはそれをキャッチするという印象を受けます。さて、その明らかな発見のために、彼の同僚といくつかのフォーラムにそのVB.NET機能を自慢します。

しかし、VB.NETプログラマーが最初のコードの意図を誤って推測するという間違いをしないようにしてください。上記のC#のコードフラグメント このように人生を始めた 最初は次のように書かれていました。

_float f = file.ReadFloat(0); 
long l = f; 
_

そして、それはコンパイルされません、C#コンパイラキャッチ互換性のない型の割り当て、_Option Strict On_を持つ同等のVB.NETもコンパイルしないのと同じように(ただし、_Option Strict_はOnに設定されていますが、長すぎます)。したがって、_(long)_を使用してfloatをlongに型キャストする必要があります。これになります:long l = (long)f;

このコードを変換するのと同じ方法で、ある変数タイプを別の互換タイプにキャストします...

_TextBox txt = (TextBox)sender;
_

...このコードへ:

_Dim txt As TextBox = DirectCast(sender, Textbox)
_

このコードを変換する必要があります...

_long l = (long)f; // will compile
_

...このコードへ:

_Dim l As Long = DirectCast(f, Long) ' will not compile
_

しかし、互換性のあるプリミティブ型間のキャストでは、コンパイルされません。これは、DirectCastが不足している場所です。上記のC#コードとは対称性がなく、Directという名前にもかかわらず、互換性のあるプリミティブ型のキャストには使用できませんキャスト

私の見方では、DirectCastはCastObjectという名前にする必要があります。これは、オブジェクトタイプ(およびオブジェクト内にボックス化されたプリミティブタイプ)間でのみキャストできるためです。 DirectCastには、互換性のあるプリミティブ型(整数、二重、およびそれらの下位および上位の対応物)を割り当てるビジネスはありません。互換性のあるプリミティブ型間で割り当てると、DirectCastは有用でなくなります。特に、とにかくバックスペースし、適切なものに置き換えます。

または、私が見る他の方法では、DirectCastコンストラクトを修正して、古い言語と新しい言語のように互換性のある型をキャストできるようにする必要があります。 C、C++、C#、Java、Delphi、Dなど。これを行うと、型キャストに関して、VB.NETが他の言語と大幅に対称的になります。これを行うと、名前がそのタイプ(CInt、CDbl、CSngなど)に直接マッピングされないすべての機能を捨てることができます(仮に、古い関数に依存する他のプログラムを失敗させることはできません)。それらの代わりにDirectCastを使用します。

0
Michael Buen

サンプルコードを本当に実行しようとしましたか?

に関して...

//This code uses a type converter to go across an inheritance tree
string s = "10";
int i = (int)s;

...実行すると仮定しました。また、実行されません

0
Michael Buen

これを試してみましょう。

まず、これについて明確にしましょう。このWILL NOTコンパイル:

//This code uses a type converter to go across an inheritance tree
string s = "10";
int i = (int)s;

VBのCType

VBでは、次を使用します。

Dim s as String = "10"
Dim i as Integer = CType(s, Integer)

C#では、次を使用します。

string s = "10";
int i = Convert.ToInt32(s);

VBのDirectCast

正しい型の場合はキャストし、そうでない場合はInvalidCastExceptionをスローします。

直接キャストはブランチを上下することしかできず、別のブランチに移動することはできません。

その説明から、C#キャストの直接的な同等物になるでしょう。ただし、C#では、キャストダウンにのみキャスト演算子を指定する必要があります。キャストアップは完全にオプションです。例:

// casting down
object t = "some random string needing to be casted down";
string s = (string) t;
// casting up
object a = s;
// explicitly casting up although it's totally optional
object b = (object) s;

C#キャストは、タイプコンバーターを探しません。キャストしようとしている型に対して定義された明示的/暗黙的な演算子オーバーロードのみを検索します。


VBのTryCast

これはキーワードとしてのC#と同等であることをすでに正しく理解しています。

0
Amry

C#には2種類のキャストがあります。追加のコードがないと、C#のDirectCastキーワードに相当するものはありません。自分で作成せずに最も近いのは、()

あなたが持っている:

My_Object c = (My_Object)object

そして

My_Object c = object as My_Object

最初のキャストでは、キャストが失敗するとエラーがスローされます。あなたは言っています、「私はこのオブジェクトが何であるかを知っています、そして、そうでないなら、何か間違っています。」

2番目では、cには可能な場合は値nullが割り当てられます(値型にnullを割り当てることはできません)。この中で、あなたは「これが何であるかは知っていると思うが、何も間違っていないかもしれないので、エラーを投げないでください」と言っています。

キャストを説明する他の投稿:

明示的な型キャストと暗黙的な型キャストの違いは何ですか?

0
kemiller2002