Разница между отливкой и использованием 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
иNotSupportedException
is (происходит от)Exception
таким образом, приведение неявно. Никакая информация не теряется, потому что объекты не меняют свои типы (это отличается отstruct
S и примитивные типы, потому что с гипсом вы создадите новый объект другого типа), какие изменения ваши 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 для этого), используя пользовательские операторы преобразования (определенные в классе для приведения) или более сложные механизмы (
TypeConverter
S или методы класса, например). Вы не знаете, что произойдет, чтобы сделать это, но вы знаете, что это может потерпеть неудачу (вот почему ИМО, когда больше контролируемые преобразование возможно вы должны использовать его). В вашем случае преобразование просто разберет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. Сделайте вызывающего абонента ответственным за предоставление правильного типа. Таким образом, компилятор делает работу за вас.