Разница между отливкой и использованием 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 ответов:
даже если вы мая увидеть их как-то эквивалентно они совершенно разные по назначению. Давайте сначала попробуем определить, что такое актерский состав:
приведение-это действие изменения сущности одного типа данных в другой.
это немного общий и это как-то эквивалентно преобразование потому что 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иNotSupportedExceptionis (происходит от)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который имеет все виды сложной логики для определения того, какие типы строк представляют какие числовые значения. Вы можно было бы написать свой собственный метод преобразования, который отображал бы строки в двойники по-разному (возможно, для поддержки некоторых совершенно разных соглашений для отображения чисел, таких как римские цифры или что-то еще). Преобразование может сделать что угодно, но идея в том, что вы на самом деле не просите компилятор сделать что-нибудь для Вас; Вы тот, кто пишет код, чтобы определить, как создать новый объект, потому что компилятор без вашей помощи не знает, как сопоставить (в качестве примера) astringв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. Сделайте вызывающего абонента ответственным за предоставление правильного типа. Таким образом, компилятор делает работу за вас.