C # эквивалентно VC.NET DirectCast? - PullRequest
49 голосов
/ 21 апреля 2010

Имеет ли C # эквивалент DirectCast VB.NET?

Мне известно, что он имеет () приведения и ключевое слово "как", но они соответствуют CType и TryCast.

Для ясности, эти ключевые слова делают следующее:

CType / () casts : Если это уже правильный тип, приведите его, в противном случае найдите преобразователь типа и вызовите его. Если преобразователь типов не найден, создайте исключение InvalidCastException.

TryCast / "as" ключевое слово : если это правильный тип, приведите его, в противном случае верните ноль.

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)

Ответы [ 10 ]

11 голосов
/ 21 апреля 2010

Кажется очевидным, что нужной вам функциональности нет в 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, назовите его так:

8 голосов
/ 21 апреля 2010

ВТОРОЕ ОБНОВЛЕНИЕ :

Хорошо, вот метод C #, который, как утверждается, предположительно делает в основном то, что DirectCast делает в VB.NET.

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, как это делает приведенный выше код. , (Если вы хотите преобразовать значение , известное как тип System.Int32, в значение типа System.Int64 в VB.NET, вы должны использовать CLng, а не DirectCast.) Если там была переменная, набранная как System.Object, затем , имеет смысл использовать DirectCast, и приведенный ниже код действительно будет эквивалентен:

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

Таким образом, я утверждаю, что DirectCast в VB.NET, в любом сценарии, в котором имеет смысл использовать его (т. Е. Когда тип объекта неизвестен во время компиляции), такой же как прямой () стиль отливки в C # .


РЕДАКТИРОВАТЬ : Ну, позор, что я опубликовал код VB, который не компилировался. После пересмотра того, что я говорил, я забираю свой второй ответ, но сохраняю первый.

Если вы имеете в виду использование DirectCast, когда вы берете объект неизвестного типа и пытаетесь привести его к желаемому типу, тогда он равен так же, как C #'s ():

VB:

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

C #:

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

Это связано с тем, что если o указано как System.Object, то операция () в C # попытается его распаковать. Это не удастся, если типы не совпадают точно; например, если o в штучной упаковке System.Double, (int)o сгенерирует исключение, потому что o должен быть распакован как System.Double, прежде чем он может быть преобразован в System.Int32 (если вы мне не верите, попробуйте сами!).


Примечание: приведенное ниже неточно, поскольку DirectCast не не выполняет расширяющие преобразования; в любом случае я оставляю это для потомков.

С другой стороны, когда речь идет о преобразованиях с расширением или сужением, использование операции () в 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;
1 голос
/ 21 апреля 2010

Вы можете реализовать это самостоятельно:

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

Можно использовать следующим образом:

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

Это работает и не включает определяемые пользователем преобразователи из-за того факта, что обобщенные значения «разрешаются» во время выполнения, но преобразования типов разрешаются во время компиляции - структура фактически не генерирует отдельные реализации для каждого T но скорее разделяет реализацию для аналогичного T, и, следовательно, среда выполнения не имеет информации для разрешения пользовательских преобразований.

1 голос
/ 21 апреля 2010

На самом деле компилятор просто отлавливает нарушение DirectCast, если обнаружит, что типизированная переменная не может быть преобразована в другой тип

Это фактические эквиваленты:

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

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

Обратите внимание на опасность этой конструкции, когда вы просто назначаете double для целого числа в VB.NET, 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) 

[EDIT]

@ Дэн Тао:

Нет необходимости использовать DirectCast в C #, среда выполнения также предотвращает загрузку длинных в целочисленные значения. Это то, что утверждает csauve, что C # не имеет DirectCast, что DirectCast может предотвратить присвоение различных типов переменных, тогда как «поскольку» C # не имеет этого DirectCast, он будет молча сообщать об ошибке при назначении различных типов. Но, как вы можете видеть, это не так, приведение C # точно такое же, как 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, если вы действительно хотите уменьшить значение переменной и хотите сделать это ортогональным способом, то есть просто запомнив одно ключевое слово, вы должны сделать это:

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

Но это не скомпилируется, так как достичь downcast для целых чисел? Вы должны помнить другие ключевые слова 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)

Если вы хотите выполнить downcasting, вы должны сделать это:

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)

0 голосов
/ 19 сентября 2018

DirectCast и () не всегда генерируют один и тот же IL, поэтому я думаю, что это разница между компиляторами VB и C #.

AFAICT, ссылочные типы приводятся с использованием инструкции castclass IL, в то время как для типов значений компилятор генерирует соответствующий IL в зависимости от типов ввода.

В C # приведение от double к integer испускает инструкцию conv.i4 IL, которая будет весело перезаписывать бит знака или что-либо еще в выводе, если значение слишком большое. В VB это ошибка компиляции.

Интересно, что если вы используете промежуточную переменную object для хранения double, то приведение не удастся как для C #, так и для VB ... но во время выполнения. Оба компилятора выдают инструкцию unbox вместо попытки выполнить преобразование.

0 голосов
/ 21 апреля 2010

Я думаю, что этот сценарий лучше всего объясняет, почему 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: «Пожалуйста, позвольте мне делать ставки, пожалуйста, скомпилируйте, пожалуйста ... !!!»

После некоторых моментов, связанных с поиском в Google и MSDN:

Программист VB.NET: «Ах .. поэтому я должен использовать эту конструкцию CLng или CType для приведения переменных»

Dim l As Long = CLng(f)

Это то, что я имел в виду под DirectCast, имеет ложное чувство безопасности проверки типов во время компиляции. DirectCast просто предназначен для возврата, если программист не знает, когда и где их следует использовать. DirectCast - это защитное одеяло, которое не носят постоянно.

Насколько полезен DirectCast в этом сценарии, если он вообще не будет использоваться?


[EDIT]

@ Jules

Я не претендую на то, что все программисты VB.NET не знают, что на самом деле использует DirectCast, некоторые действительно знают, что DirectCast просто предназначены для использования с объектными типами (и примитивными типами, которые заключены в рамки объект) только.

Один из сценариев, когда кодировщик VB.NET, перекодирующий существующий код C # в VB.NET, приведет к неверному выводу, заключается в ожидаемой (правильной или нет) языковой симметрии друг с другом.

Когда он видит в коде эту конструкцию ...

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, он будет расстроен очевидным отсутствием симметрии здесь.

Но для VB.NET, которому поручено преобразовывать код C # в VB.NET, у него сложится впечатление, что компилятор C # не перехватывает несовместимые назначения типов, а VB.NET его перехватывает. Теперь, для этого очевидного открытия, похвастаюсь этой функцией VB.NET своим коллегам и некоторым форумам.

Но чтобы программист VB.NET допустил ошибку, неверно определив намерение первого кода. Фрагмент кода C # выше начал свою жизнь так: изначально был написан так:

float f = file.ReadFloat(0); 
long l = f; 

И это не скомпилируется, компилятор C # отлавливает несовместимые назначения типов в том же духе, что эквивалентный VB.NET с Option Strict On также не скомпилирует это (хотя и не будет скомпилироваться, когда Option Strict установлено на On, слишком снисходительно). Таким образом, нам нужно типизировать float для long, используя (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 Cast .

На мой взгляд, DirectCast должен называться CastObject , поскольку он в любом случае может выполнять приведение только между типами объектов (а также примитивными типами, заключенными в объекты). На самом деле DirectCast не имеет никакого дела с назначением совместимых примитивных типов (целочисленных, двойных и их младших и старших аналогов). При назначении между совместимыми типами примитивов DirectCast перестает быть полезным, особенно если вы все равно откажетесь от него и замените его на правильный.

Или, с другой стороны, необходимо изменить конструкцию DirectCast, чтобы она могла приводить совместимые типы, как это делают старые и новые языки с тех пор, например. C, C ++, C #, Java, Delphi, D и т. Д. В этом случае VB.NET предложит значительную симметрию другим языкам, когда речь идет о приведении типов. Делая это, мы также можем отбросить (только гипотетически, мы не можем заставить другие программы терпеть неудачу, которые полагаются на старые функции) всего множества функций, имена которых не соответствуют непосредственно его типам (например, CInt, CDbl, CSng и т. Д.) будет просто использовать DirectCast вместо них.

0 голосов
/ 21 апреля 2010

Позвольте мне попробовать это.

Во-первых, позвольте мне прояснить это. Этот НЕ БУДЕТ компилировать:

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

VB's CType

В VB вы должны использовать:

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

В C # я бы использовал:

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

VB's 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 # cast не ищет конвертер типов. Он будет искать только определенные явные / неявные перегрузки операторов для типа, к которому вы пытаетесь привести.


VB's TryCast

Вы уже правильно поняли, что это эквивалентно C # в качестве ключевого слова.

0 голосов
/ 21 апреля 2010

Вы действительно пытались запустить пример кода?

Что касается ...

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

... Вы утверждали, что это будет работать. Также не запускается

0 голосов
/ 21 апреля 2010

У вас есть два типа приведения в C #.Без дополнительного кода нет эквивалента ключевому слову DirectCast в C #.Самое близкое, что у вас есть, не создавая его самостоятельно, это использовать ().

. У вас есть:

My_Object c = (My_Object)object

и

My_Object c = object as My_Object

В первом случае, еслиприведение не удается, выдает ошибку.Вы говорите: «Я знаю, что это за объект, и если это не так, то что-то не так».

Во втором c присваивается значение null, где это возможно (null не может бытьприсваивается типам значений).В этом вы говорите: «Я думаю, я знаю, что это такое, но если нет, не выдавайте ошибку, потому что ничего не может быть не так».

Другой пост, объясняющий кастинг:

В чем разница между явным и неявным приведением типов?

0 голосов
/ 21 апреля 2010

Приведение () должно быть таким же; это вызывает InvalidCastException. Просто попробуйте это в C #:

 string t = "hello";
 object x = t;
 int j = (int) x;
...