Разница между отливкой и использованием Convert.To () метод


у меня есть функция, которая бросает double on string значения.

string variable = "5.00"; 

double varDouble = (double)variable;

изменение кода было возвращено, и проект строится с ошибкой:System.InvalidCastException: Specified cast is not valid.

однако, после выполнения следующих действий...

string variable = "5.00"; 

double varDouble = Convert.ToDouble(variable);

...проект строится без каких-либо ошибок.

в чем разница между литьем и с помощью Convert.To() способ? почему кастинг бросает Exception и с помощью Convert.To() не?

9 53

9 ответов:

даже если вы мая увидеть их как-то эквивалентно они совершенно разные по назначению. Давайте сначала попробуем определить, что такое актерский состав:

приведение-это действие изменения сущности одного типа данных в другой.

это немного общий и это как-то эквивалентно преобразование потому что cast часто имеет тот же синтаксис преобразования, поэтому вопрос должен быть когда приведение (неявное или явное) разрешено языком и когда вы должны использовать (более) явное преобразование?

позвольте мне сначала ничья простая линия между ними. Формально (даже если эквивалентно для синтаксиса языка) приведение изменит тип, а преобразование будет / может изменить значение (в конечном итоге вместе тип). Также приведение является обратимым, в то время как преобразование не может быть.

эта тема довольно обширна, поэтому давайте попробуем немного сузить исключение пользовательских операторов приведения из игры.

неявные приведения

в C# приведение неявные, когда вы не потеряете никакой информации (обратите внимание, что эта проверка выполняется с типами, а не с их фактическими значениями).

примитивные типы

например:

int tinyInteger = 10;
long bigInteger = tinyInteger;

float tinyReal = 10.0f;
double bigReal = tinyReal;

эти приведения неявны, потому что во время преобразования вы не потеряете никакой информации (вы просто делаете тип шире). Наоборот, неявное приведение не допускается, потому что, независимо от их фактических значений (поскольку они могут быть проверены только во время выполнения), во время преобразования вы можете потерять некоторую информацию. Например, этот код не будет компилироваться, потому что double может содержать (и на самом деле это так) значение, не представимое с помощью float:

double bigReal = Double.MaxValue;
float tinyReal = bigReal;

объекты

в случае объекта (указатель на) приведение всегда неявно, когда компилятор может быть уверен, что тип источника является производным класс (или он реализует) тип целевого класса, например:

string text = "123";
IFormattable formattable = text;

NotSupportedException derivedException = new NotSupportedException();
Exception baseException = derivedException;

в этом случае компилятор знает это string осуществляет IFormattable и NotSupportedException is (происходит от) Exception таким образом, приведение неявно. Никакая информация не теряется, потому что объекты не меняют свои типы (это отличается от structS и примитивные типы, потому что с гипсом вы создадите новый объект другого типа), какие изменения ваши view из них.

явные приведения

приведение является явным, когда преобразование не выполняется неявно компилятором, а затем необходимо использовать оператор приведения. Обычно это означает, что:

  • вы можете потерять информацию или данные, так что вы должны быть в курсе.
  • преобразование может завершиться неудачей (потому что вы не можете конвертировать один тип в другой), так что, опять же, вы должны знать, что вы делающий.

примитивные типы

явное преобразование требуется для примитивных типов, когда во время конвертации вы можете потерять некоторые данные, например:

double precise = Math.Cos(Math.PI * 1.23456) / Math.Sin(1.23456);
float coarse = (float)precise;

float epsilon = (float)Double.Epsilon;

в обоих примерах, даже если значения в пределах float диапазон, вы потеряете информацию (в этом случае точность), поэтому преобразование должно быть явным. Теперь попробуйте это:

float max = (float)Double.MaxValue;

это преобразование потерпит неудачу, поэтому, опять же, оно должно быть явным, чтобы вы знали об этом, и вы можете это сделать проверка (в примере значение является постоянным, но оно может исходить из некоторых вычислений во время выполнения или ввода-вывода). Вернемся к вашему примеру:

string text = "123";
double value = (double)text;

этот код не будет компилироваться, потому что компилятор не может преобразовать текст в числа. Текст может содержать любые символы, а не только числа, и это слишком много, в C#, даже для явного приведения (но это может быть разрешено на другом языке).

объекты

преобразование из указателей (в объекты) может завершиться ошибкой, если типы не связаны, например этот код не компилируется (потому что компилятор знает, что нет никакого возможного преобразования):

string text = (string)AppDomain.Current;
Exception exception = (Exception)"abc";

этот код будет компилироваться, но он может завершиться ошибкой во время выполнения (это зависит от эффективного типа приведенных объектов) с InvalidCastException:

object obj = GetNextObjectFromInput();
string text = (string)obj;

obj = GetNextObjectFromInput();
Exception exception = (Exception)obj;

преобразование

Итак, наконец, если приведения являются преобразованием, то зачем нам нужны такие классы, как несколько? Игнорируя тонкие различия, которые исходят от Convert реализация и IConvertible реализаций на самом деле, потому что в C# с приведением вы говорите компилятору:

поверьте мне, этот тип-это тот тип, даже если вы не можете знать его сейчас, позвольте мне сделать это, и вы увидите.

или

не волнуйся, мне все равно что-то будет потеряно в этом преобразовании.

для всего остального a больше требуется явная операция (подумайте о последствиях легко бросает, вот почему C++ ввел для них длинный, подробный и явный синтаксис). Это может включать в себя сложную операцию (для string ->double преобразование разбор будет необходим). Преобразование в string, например, всегда возможно (через ToString() метод), но это может означать что-то отличное от того, что вы ожидаете, поэтому оно должно быть более явным, чем приведение (чем больше ты пишешь, тем больше думаешь о том, что делаешь).

это преобразование может быть сделано внутри объекта (используя известные инструкции IL для этого), используя пользовательские операторы преобразования (определенные в классе для приведения) или более сложные механизмы (TypeConverterS или методы класса, например). Вы не знаете, что произойдет, чтобы сделать это, но вы знаете, что это может потерпеть неудачу (вот почему ИМО, когда больше контролируемые преобразование возможно вы должны использовать его). В вашем случае преобразование просто разберет string для производства double:

double value = Double.Parse(aStringVariable);

конечно, это может не сработать, так что если вы это сделаете вы всегда должны поймать исключение, которое он может бросить (FormatException). Это не по теме здесь, но если когда TryParse доступно, то вы должны использовать его (потому что семантически вы сказать это может быть не число, а даже faster...to неудача).

конверсии в .NET могут поступать из многих мест,TypeConverter, неявные / явные приведения с определяемыми пользователем операторами преобразования, реализация IConvertible и методы разбора (я что-то забыл?). Взгляните на MSDN для подробнее о них.

чтобы закончить этот длинный ответ, всего несколько слов, определенных пользователем операторов преобразования. Это просто сахар чтобы дать программисту использовать приведение для преобразования одного типа в другой. Это метод внутри класса (тот, который будет приведен), который говорит: "Эй, если он/она хочет преобразовать этот тип в этот тип, то я могу это сделать". Например:

float? maybe = 10; // Equals to Nullable<float> maybe = 10;
float sure1 = (float)maybe; // With cast
float sure2 = maybe.Value; // Without cast

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

EasyString text = "123"; // Implicit from string
double value = (string)text; // Explicit to double

в вашей реализации вы можете решить "сделать жизнь программиста проще" и выставить это преобразование через приведение (помните, что это просто ярлык для записи меньше). Некоторые языки могут даже позволить это:

double value = "123";

разрешение неявного преобразования в любой тип (проверка будет выполнена во время выполнения). С правильными опциями это можно сделать, например, в VB.NET - это просто ... другая философия.

что я могу с ними сделать?

Итак, последний вопрос заключается в том, когда вы должны использовать тот или иной. Давайте посмотрим, когда вы можете использовать явное приведение:

  • преобразования между базовыми типами.
  • преобразование object к любому другому типу (это может включать распаковку тоже).
  • преобразование из производного класса в базовый класс (или реализованный интерфейс).
  • переходы из одного введите другой через пользовательские операторы преобразования.

только первое преобразование может быть сделано с Convert поэтому для других у вас нет выбора, и вам нужно использовать явное приведение.

давайте посмотрим теперь, когда вы можете использовать Convert:

  • преобразования из любого базового типа в другой базовый тип (с некоторыми ограничениями, см. MSDN).
  • преобразование любого типа, который реализует IConvertible любой другой (поддерживаемых типа.
  • преобразования из/в byte массив в / из строки.

выводы

ИМО Convert следует использовать каждый раз, когда вы знаете, что преобразование может завершиться ошибкой (из-за формата, из-за диапазона или потому, что оно может не поддерживаться), даже если одно и то же преобразование можно выполнить с помощью приведения (если только что-то другое не доступно). это дает понять, кто будет читать ваш код, каковы ваши намерения и что он может потерпеть неудачу (упрощение отладки).

для всего остального вам нужно использовать бросок, нет выбора, но если есть другой лучший метод, то я предлагаю вам использовать его. В вашем примере преобразование из string до double это то ,что (особенно если текст поступает от пользователя) очень часто будет терпеть неудачу, поэтому вы должны сделать его как можно более явным (кроме того, вы получаете больше контроля над ним), например, используя TryParse метод.

Edit: в чем разница между их?

согласно обновленному вопросу и сохраняя то, что я написал раньше (о , когда вы можете использовать приведение по сравнению с тем, когда вы можете / должны использовать Convert) тогда последний пункт, чтобы уточнить, если есть разница между ними (более того Convert использует IConvertible и IFormattable интерфейсы, поэтому он может выполнять операции, не допускается муляжи).

короткий ответ:да, они ведут себя по-разному. Я вижу Convert класс как вспомогательные методы класс так часто он предоставляет некоторые пользу или немного другое поведение. Например:

double real = 1.6;
int castedInteger = (int)real; // 1
int convertedInteger = Convert.ToInt32(real); // 2

довольно разные, верно? Литой усекает (это то, что мы все ожидаем), но Convert выполняет округление до ближайшего целого числа (и этого можно не ожидать, если вы не знаете об этом). Каждый метод преобразования вводит различия, так что правило не может быть применено, и они должны рассматриваться в каждом конкретном случае...19 базовых типов для преобразования в любой другой тип...список может быть довольно долго, гораздо лучше проконсультироваться с MSDN в каждом конкретном случае!

кастинг-это способ сказать компилятору: "я знаю, что вы думаете, что эта переменная является баром, но я знаю больше, чем вы; объект на самом деле Foo, поэтому позвольте мне относиться к нему так, как если бы это был Foo с этого момента."Тогда, во время выполнения, если фактический объект оказался действительно Foo, тогда ваш код работает, если выясняется, что объект вообще не был Foo, тогда вы получаете исключение. (В частности,System.InvalidCastException.)

преобразование с другой стороны-это способ сказать, "если вы дайте мне объект типа Bar я могу создать совершенно новый объект Foo, который представляет то, что находится в этом объекте Bar. Я не буду изменять исходный объект, он не будет относиться к исходному объекту по-другому, он будет создать что-то новое, которое просто основано на каком-то другом значении. Что касается того, как это будет сделано, это может быть что угодно. В случае Convert.ToDouble он будет в конечном итоге вызов Double.Parse который имеет все виды сложной логики для определения того, какие типы строк представляют какие числовые значения. Вы можно было бы написать свой собственный метод преобразования, который отображал бы строки в двойники по-разному (возможно, для поддержки некоторых совершенно разных соглашений для отображения чисел, таких как римские цифры или что-то еще). Преобразование может сделать что угодно, но идея в том, что вы на самом деле не просите компилятор сделать что-нибудь для Вас; Вы тот, кто пишет код, чтобы определить, как создать новый объект, потому что компилятор без вашей помощи не знает, как сопоставить (в качестве примера) a string в double.

Итак, когда вы обращаете, и когда вы бросаете? В обоих случаях у нас есть некоторая переменная типа, скажем, A, и мы хотим иметь переменную типа B. Если наш объект A действительно, на самом деле, под капотом, является B, то мы бросаем. Если это не совсем B, то нам нужно преобразовать его и определить, как программа должна получить B от A.

The Convert.Double метод на самом деле просто внутренне называет Double.Parse(string) метод.

ни String тип, ни Double тип определите явное / неявное преобразование между двумя типами, поэтому приведение всегда будет неудачным.

The Double.Parse метод будет смотреть на каждый символ в string и построить числовое значение на основе значений символов в string. Если какой-либо из символов является недопустимым, то Parse метод терпит неудачу (в результате чего Convert.Double метод провал тоже).

в вашем примере вы пытаетесь привести строку к двойному (нецелому типу).

для его работы требуется явное преобразование.

и я должен отметить, что вы могли бы использовать Convert.ToDouble вместо Convert.ToInt64 Как вы можете потерять дробные части двойного значения при преобразовании в int.

Если ваша переменная имеет значение "5.25" varDouble было бы 5.00 (потеря 0.25 из-за преобразования в Типа int64)

чтобы ответить на ваш вопрос о литье против преобразования.

ваше приведение (явное приведение) не соответствует требованиям для явного приведения. значение, которое вы пытаетесь привести с помощью оператора cast, недопустимо (т. е. нецелочисленно).

посетить этот страница MSDN для правил литья / преобразования

приведение не предполагает никакого преобразования, т. е. внутреннее представление значения не изменяется. Пример:

object o = "Hello"; // o is typed as object and contains a string.
string s = (string)o; // This works only if o really contains a string or null.

вы можете преобразовать double до string такой

double d = 5;
string s = d.ToString(); // -> "5"

// Or by specifying a format
string formatted = d.ToString("N2"); // -> "5.00"

вы можете преобразовать string до double несколькими способами (здесь только два из них):

string s = "5";
double d = Double.Parse(s); // Throws an exception if s does not contain a valid number

или так

string s = "5";
double d;
if (Double.TryParse(s, out d)) {
    Console.WriteLine("OK. Result = {0}", d);
} else {
    Console.WriteLine("oops!");
}

С MSDN:

явные преобразования (приведения): явные преобразования требуют оператора приведения. Кастинг требуется , когда информация может быть потеряна при преобразовании, или когда преобразование не может быть успешным по другим причинам. Типичные примеры включают числовое преобразование в тип, который имеет меньшую точность или меньший диапазон, и преобразование экземпляра базового класса в производный класс.

рассмотреть следующий пример:

double a = 2548.3;
int b;
b = (int)a; //2548 --> information (.3) lost in the conversion

а также:

приведение-это способ явного информирования компилятора о том, что вы собираетесь выполнить преобразование и что вы знаете, что может произойти потеря данных.

вы могли бы использовать System.Convert класс, когда вы хотите конвертировать между несовместимых типы. Элемент главное отличие между литье и преобразование и compile и времени. Исключения преобразования типов отображаются по адресу времени, т. е. приведение типа, которое не выполняется во время выполнения, вызовет InvalidCastException быть брошенным.


вывод: в кастинге вы говорите компилятору, что a - это действительно типа b и если такой проект строится без каких-либо ошибок, как в этом примере:
double s = 2;
int a = (int) s;

но в преобразовании вы говорите компилятору, что есть способ создать новый объект из a типа b, пожалуйста, сделайте это и проект строит без ошибок, но как я уже сказал если приведение типа завершится неудачей во время выполнения, это вызовет InvalidCastException выбросить.

например, приведенный ниже код никогда не компилируется, потому что компилятор обнаруживает, что не может привести выражение типа DateTime типа int:

DateTime s = DateTime.Now;
int a = (int)(s);

но этот компилируется успешно:

DateTime s = DateTime.Now;
int a = Convert.ToInt32(s);

но во время выполнения, вы получите InvalidCastException что говорит:

недопустимый литой из 'Датавремя' до 'типа int32'.

string variable = "5.00";     
double varDouble = (double)variable;

выше преобразование просто не допускается языком. Вот список явных приведений для числовых типов:http://msdn.microsoft.com/en-us/library/yht2cx7b.aspx Как вы можете видеть, даже не каждый числовой тип может быть преобразован в другой числовой тип

еще немного информации о кастинге здесь

и как это отличается для преобразования.ToDouble ()?

при приведении типа структура данных не измениться. Ну, в случае преобразования числовых значений вы можете потерять несколько бит или получить несколько дополнительных 0 бит. но вы все еще работаете с номером. вы просто меняете объем памяти, занимаемый этим номером. Это достаточно безопасно для компилятора сделать все необходимое.

но когда вы пытаетесь привести строку к числу, вы не можете этого сделать, потому что недостаточно изменить объем памяти, занимаемой переменной. Например, 5.00 как строку представляет собой последовательность "чисел": 53(5) 46(.) 48(0) 48(0) - то есть для ASCII, но строка будет содержать что-то подобное. Если компилятор просто возьмет первый N (4 для double? не уверен) байты из строки - эта часть будет содержать совершенно другое двойное число. В то же время конвертировать.Todouble () запускает специальный алгоритм, который возьмет каждый символ строки, вычислит цифру, которую он представляет, и сделает для вас двойное число, если строка представляет собой число. Такие языки, как PHP, будут, грубо говоря кстати, звоните новообращенным.ToDouble для вас в фоновом режиме. Но C#, как и статически типизированный язык, не сделает этого за вас. Это позволяет вам быть уверенным, что любая операция является типобезопасной, и вы не получите что-то неожиданное, делая что-то вроде:

double d = (double)"zzzz"

приведение строки к двойному типу не допускается C#, поэтому вы получаете исключение, вам нужно преобразовать строку (MSDN doc это показывает приемлемые пути преобразования). Это просто потому, что строка не обязательно будет содержать числовые данные, но различные числовые типы (исключая нулевые значения). А Convert будет запущен метод, который будет проверять строку, чтобы увидеть, если он может быть превращен в числовое значение. Если это возможно, то он вернет это значение. Если он не может, это вызовет исключение.

преобразовать его, у вас есть несколько вариантов. Вы использовали Convert метод в вашем вопросе, есть Parse, который во многом похож на Convert, но вы также должны смотреть на TryParse что позволит вам сделать:

string variable = "5.00"; 

double varDouble;

if (Double.TryParse(variable, out varDouble)) {
    //Code that runs if the conversion succeeded.
} else {
    //Code that runs if the conversion failed.
}

это позволит избежать возможного исключения если вы попытаетесь Convert или Parse нечисловая строка.

double varDouble = (double)variable полагает, что variable уже является двойной. Если variable не является двойным (это строка), то это не удастся. double varDouble = Convert.ToDouble(variable) делает, как он говорит, - это преобразует. если он может разобрать или иным образом извлечь двойной из variable затем он будет.

Я второй с помощью Double.Parse или Double.TryParse потому что это более четко указывает на то, что должно происходить. Вы начинаете со строки и ожидаете, что она будет конвертирована в двойную. Если есть какие-либо сомнения, используйте TryParse.

если variable является аргументом метода, измените тип на double. Сделайте вызывающего абонента ответственным за предоставление правильного типа. Таким образом, компилятор делает работу за вас.