У меня есть функция , которая проливает double
на 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()
нет?
Ответы:
Даже если вы можете рассматривать их как эквивалентные, они совершенно разные по назначению. Давайте сначала попробуем определить, что такое приведение:
Это немного общий характер и в какой-то мере эквивалентен преобразованию, потому что приведение часто имеет тот же синтаксис преобразования, поэтому вопрос должен заключаться в том, когда преобразование (неявное или явное) разрешено языком и когда вам нужно использовать ( подробнее) явное преобразование?
Позвольте мне сначала провести простую линию между ними. Формально (даже если это эквивалентно синтаксису языка) преобразование изменит тип, в то время как преобразование изменит / может изменить значение (в конечном итоге вместе с типом). Также приведение обратимо, а преобразование - нет.
Эта тема довольно обширна, поэтому давайте попробуем немного сузить ее, исключив из игры пользовательские операторы приведения.
Неявные приведения
В C # приведение неявно, если вы не потеряете никакой информации (обратите внимание, что эта проверка выполняется с типами, а не с их фактическими значениями ).
Примитивные типы
Например:
int tinyInteger = 10; long bigInteger = tinyInteger; float tinyReal = 10.0f; double bigReal = tinyReal;
Эти приведения являются неявными, потому что во время преобразования вы не потеряете никакой информации (вы просто сделаете тип шире). И наоборот, неявное приведение типов недопустимо, потому что, независимо от их фактических значений (потому что они могут быть проверены только во время выполнения), во время преобразования вы можете потерять некоторую информацию. Например, этот код не будет компилироваться, потому что a
double
может содержать (и фактически содержит) значение, не представимое с помощьюfloat
:// won't compile! double bigReal = Double.MaxValue; float tinyReal = bigReal;
Объекты
В случае объекта (указателя на) приведение всегда неявно, когда компилятор может быть уверен, что исходный тип является производным классом (или он реализует) тип целевого класса, например:
string text = "123"; IFormattable formattable = text; NotSupportedException derivedException = new NotSupportedException(); Exception baseException = derivedException;
В этом случае компилятор знает, что
string
реализуетIFormattable
и чтоNotSupportedException
является (производным),Exception
поэтому приведение является неявным. Никакая информация не теряется, потому что объекты не меняют свои типы (это отличается отstruct
s и примитивных типов, потому что с приведением вы создаете новый объект другого типа ), какие изменения - это ваше представление о них.Явные приведения
Приведение является явным, когда преобразование не выполняется неявно компилятором, а затем необходимо использовать оператор приведения. Обычно это означает, что:
Примитивные типы
Явное приведение требуется для примитивных типов, когда во время преобразования вы можете потерять некоторые данные, например:
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;
Это преобразование завершится ошибкой, поэтому, опять же, оно должно быть явным, чтобы вы знали об этом и могли выполнить проверку (в этом примере значение является постоянным, но оно может быть получено в результате некоторых вычислений во время выполнения или ввода-вывода). Вернемся к вашему примеру:
// won't compile! string text = "123"; double value = (double)text;
Это не будет компилироваться, потому что компилятор не может преобразовать текст в числа. Текст может содержать любые символы, а не только цифры, и в C # это слишком много, даже для явного приведения (но это может быть разрешено на другом языке).
Объекты
Преобразования из указателей (в объекты) могут завершиться ошибкой, если типы не связаны, например, этот код не будет компилироваться (потому что компилятор знает, что возможное преобразование невозможно):
// won't compile! string text = (string)AppDomain.Current; Exception exception = (Exception)"abc";
Этот код будет компилироваться, но может завершиться ошибкой во время выполнения (это зависит от эффективного типа приведенных объектов) с
InvalidCastException
:object obj = GetNextObjectFromInput(); string text = (string)obj; obj = GetNextObjectFromInput(); Exception exception = (Exception)obj;
Конверсии
Итак, наконец, если приведение является преобразованием, тогда зачем нам нужны классы вроде
Convert
? Игнорирование тонких различий, связанных сConvert
реализацией иIConvertible
реализациями на самом деле, потому что в C # с приведением вы говорите компилятору:-или-
Для всего остального требуется более явная операция (подумайте о последствиях простых приведений , поэтому C ++ ввел для них длинный, подробный и явный синтаксис). Это может потребовать сложной операции (для
string
->double
преобразования потребуется синтаксический анализ). Преобразованиеstring
, например, в всегда возможно (с помощьюToString()
метода), но оно может означать нечто иное, чем вы ожидаете, поэтому оно должно быть более явным, чем приведение (чем больше вы пишете, тем больше думаете о том, что делаете ).Это преобразование может быть выполнено внутри объекта (используя для этого известные инструкции IL), используя пользовательские операторы преобразования (определенные в классе для приведения) или более сложные механизмы (
TypeConverter
например, методы или методы класса). Вы не знаете, что произойдет с этим, но вы знаете, что это может потерпеть неудачу (поэтому IMO, когда возможно более контролируемое преобразование, вы должны его использовать). В вашем случае преобразование просто проанализирует,string
чтобы создатьdouble
:double value = Double.Parse(aStringVariable);
Конечно, это может потерпеть неудачу, поэтому, если вы это сделаете, вы всегда должны перехватить исключение, которое оно может выбросить (
FormatException
). Здесь это не по теме, но когдаTryParse
доступен, вы должны использовать его (потому что семантически вы говорите, что это может быть не число, и это еще быстрее ... потерпеть неудачу).Конверсии в .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
В этом случае это явно, потому что это может потерпеть неудачу, но это разрешено реализации (даже если есть рекомендации по этому поводу). Представьте, что вы пишете собственный строковый класс следующим образом:
EasyString text = "123"; // Implicit from string double value = (string)text; // Explicit to double
В своей реализации вы можете решить «облегчить жизнь программисту» и раскрыть это преобразование через приведение (помните, что это просто ярлык, чтобы писать меньше). Некоторые языки могут даже допускать это:
double value = "123";
Разрешение неявного преобразования в любой тип (проверка будет выполняться во время выполнения). С соответствующими параметрами это можно сделать, например, в VB.NET. Это просто другая философия.
Что мне с ними делать?
Итак, последний вопрос: когда использовать тот или иной? Посмотрим, когда можно использовать явное приведение:
object
в любой другой тип (это также может включать распаковку).Может быть выполнено только первое преобразование,
Convert
поэтому для остальных у вас нет выбора, и вам нужно использовать явное приведение.Теперь посмотрим, когда можно будет использовать
Convert
:IConvertible
в любой другой (поддерживаемый) тип.byte
массив в / из строки.Выводы
IMO
Convert
следует использовать каждый раз, когда вы знаете, что преобразование может завершиться неудачно (из-за формата, из-за диапазона или из-за того, что оно может быть неподдерживаемым), даже если такое же преобразование может быть выполнено с преобразованием (если не доступно что-то еще). Он дает понять, кто будет читать ваш код, каковы ваши намерения и что он может потерпеть неудачу (упрощая отладку).Для всего остального вам нужно использовать приведение, выбора нет, но если доступен другой лучший метод, я предлагаю вам использовать его. В вашем примере преобразование из
string
вdouble
- это то, что (особенно если текст поступает от пользователя) очень часто терпит неудачу, поэтому вы должны сделать его как можно более явным (более того, вы получите больше контроля над ним), например, используяTryParse
метод.Изменить: в чем разница между ними?
Согласно обновленному вопросу и сохранению того, что я написал ранее (о том, когда вы можете использовать приведение по сравнению с тем, когда вы можете / должны использовать
Convert
), последний момент, который нужно уточнить, - есть ли разница между ними (кроме того,Convert
используетIConvertible
иIFormattable
интерфейсы, чтобы он мог выполнять операции не допускается с приведениями).Короткий ответ - да, они по-разному ведут себя . Я вижу этот
Convert
класс как класс вспомогательных методов, поэтому часто он дает некоторые преимущества или немного другое поведение. Например:double real = 1.6; int castedInteger = (int)real; // 1 int convertedInteger = Convert.ToInt32(real); // 2
Совсем другое дело, правда? Приведение обрезается (это то, что мы все ожидаем), но
Convert
выполняет округление до ближайшего целого числа (чего нельзя ожидать, если вы этого не знаете). Каждый метод преобразования имеет различия, поэтому общее правило не может применяться, и их нужно рассматривать в каждом конкретном случае ... 19 базовых типов для преобразования в любой другой тип ... список может быть довольно длинным, гораздо лучше проконсультироваться с случаем MSDN, кейс!источник
Difference between casting and using the Convert.To() method
. В противном случае очень исчерпывающий ответ. (Надеюсь, мой вопросdouble
значения, не представляющие целые числа, должны быть «конвертируемы»int
. Приведение могло бы показаться подходящей парадигмой в тех случаях, когда, например, извлекаютсяInt32
значения из a,double[]
который содержит смесь реальных чисел иInt32
значений, которые были преобразованы вdouble
[попытка преобразовать значение, которое невозможно точно представить в,int32
будет указывать на неожиданное состояние и должен вызывать исключение], но я бы подумал, что когда кто-то хочет преобразование с потерями, нужно конкретизировать желаемую форму.object o = 123; var l = Convert.ToInt64(o); var i = (long) (int) o; var f = (long) o // InvalidCastException
float
->int
), а принуждение . Например, приведение может бытьDerivedClass
->BaseClass
. Это сбивает с толку, потому что в C # мы используем одно и то же слово (и оператор) для обоих, но на самом деле это разные вещи. Формальное определение для их различения немного сложнее, чем то, что я написал.Приведение - это способ сказать компилятору: «Я знаю, что вы думаете, что эта переменная - это Bar, но я знаю больше, чем вы; объект на самом деле является Foo, поэтому позвольте мне рассматривать его, как если бы это был Foo из сейчас на." Затем во время выполнения, если фактический объект оказался действительно Foo, тогда ваш код работает, если окажется, что объект вообще не был Foo, вы получите исключение. (В частности, ан
System.InvalidCastException
.)С другой стороны, преобразование - это способ сказать: «Если вы дадите мне объект типа Bar, я могу создать новый объект Foo, который представляет то, что находится в этом объекте Bar. Я не буду менять исходный объект, он выиграл» Если относиться к исходному объекту по-другому, он создаст что-то новое, основанное только на каком-то другом значении . Что касается того, как он будет это делать, это может быть что угодно. В этом случае
Convert.ToDouble
он вызоветDouble.Parse
который имеет всевозможную сложную логику для определения того, какие типы строк представляют какие числовые значения. Вы можете написать свой собственный метод преобразования, который по-разному сопоставлял строки с двойниками (возможно, для поддержки совершенно другого соглашения об отображении чисел, таких как римские цифры или что-то еще). Преобразование может делать что угодно, но идея в том, что на самом деле вы не просите компилятор что-либо делать за вас; вы пишете код, чтобы определить, как создать новый объект, потому что компилятор без вашей помощи не знает, как сопоставить (например) astring
с adouble
.Итак, когда вы конвертируете, а когда кастуете? В обоих случаях у нас есть некоторая переменная типа, скажем, A, и мы хотим иметь переменную типа B. Если наш объект A действительно, на самом деле, скрытый под капотом, является B, тогда мы приводим. Если на самом деле это не B, то нам нужно преобразовать его и определить, как программа должна получать B от A.
источник
foreach
). За исключением этих исключений приведение типов по определению является явным.Откуда
MSDN
:Рассмотрим следующий пример:
double a = 2548.3; int b; b = (int)a; //2548 --> information (.3) lost in the conversion
А также:
Вы можете использовать
System.Convert
class, когда хотите конвертировать между несовместимыми типами. Основное различие между литьем и обращенным в компиляции и время выполнения . Исключения преобразования типа появляются во время выполнения , т. Е. Приведение типа, которое завершается неудачно во время выполнения, вызовет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
следующее:источник
На
Convert.Double
самом деле метод просто вызываетDouble.Parse(string)
метод внутри себя .Ни
String
тип, ниDouble
тип не определяют явное / неявное преобразование между двумя типами, поэтому приведение типов всегда завершается ошибкой.Double.Parse
Метод будет смотреть на каждый символ вstring
и построить числовое значение , основанное на значениях символов вstring
. Если какой-либо из символов недействителен,Parse
метод не работает (что также приводитConvert.Double
к сбою метода).источник
Convert.ToDouble()
выйти за рамки байтов и рассмотреть данные?В вашем примере вы пытаетесь привести строку к двойному (нецелочисленному типу).
Для его работы требуется явное преобразование.
И я должен указать, что вы могли использовать
Convert.ToDouble
вместо,Convert.ToInt64
поскольку вы можете потерять дробные части двойного значения при преобразовании в int.если ваша переменная имеет значение «5,25», varDouble будет 5,00 (потеря 0,25 из-за преобразования в Int64)
Чтобы ответить на ваш вопрос о кастинге и конвертации.
Ваше приведение (явное приведение) не соответствует требованиям для явного приведения. значение, которое вы пытаетесь преобразовать с помощью оператора приведения, недопустимо (т.е. не является целым).
Посетите эту страницу 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.
Вы можете преобразовать a
double
вstring
этоdouble d = 5; string s = d.ToString(); // -> "5" // Or by specifying a format string formatted = d.ToString("N2"); // -> "5.00"
Вы можете преобразовать a
string
в adouble
несколькими способами (здесь только два из них):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!"); }
источник
Convert.ToDouble()
внутренние звонкиDouble.Parse()
. В моих интересах использоватьConvert.ToDouble()
overDouble.Parse()
или нет и почему?Convert.ToDouble
имеет много перегрузок, которые принимают разные типы ввода. Принятие перегрузкиstring
возвращается,0.0
еслиnull
передана строка. Кроме того, я не вижу преимуществ в его использовании.Double.Parse()
мне есть что предложить?Double.Parse()
прям чемConvert.ToDouble()
. Если вы уверены, что ваша строка будет содержать действительный номер, вы можете смело использовать его, в противном случае я советую вам использоватьDouble.TryParse
.string variable = "5.00"; double varDouble = (double)variable;
Вышеуказанное преобразование просто запрещено языком. Вот список явных приведений для числовых типов: http://msdn.microsoft.com/en-us/library/yht2cx7b.aspx Как видите, даже не каждый числовой тип можно преобразовать в другой числовой тип.
Дополнительная информация о кастинге здесь
При приведении типа структура данных не изменяется. Что ж, в случае преобразования числовых значений вы можете потерять несколько бит или получить несколько дополнительных 0 бит. Но вы все еще работаете с числом. Вы просто меняете объем памяти, занимаемый этим числом. Это достаточно безопасно, чтобы компилятор делал все необходимое.
Но когда вы пытаетесь преобразовать строку в число, вы не можете этого сделать, потому что недостаточно изменить объем памяти, занятой переменной. Например, 5.00как строка представляет собой последовательность «чисел»: 53 (5) 46 (.) 48 (0) 48 (0) - это для ASCII, но строка будет содержать нечто подобное. Если компилятор просто возьмет первые N (4 для двойного? Не уверен) байт из строки - этот кусок будет содержать совершенно другое двойное число. В то же время Convert.ToDouble () запускает специальный алгоритм, который берет каждый символ строки, вычисляет цифру, которую он представляет, и делает для вас двойное число, если строка представляет собой число. Грубо говоря, такие языки, как PHP, будут вызывать для вас Convert.ToDouble в фоновом режиме. Но C #, как и язык со статической типизацией, не сделает этого за вас. Это позволяет вам быть уверенным, что любая операция безопасна по типу, и вы не получите чего-то неожиданного, сделав что-то вроде:
double d = (double)"zzzz"
источник
Преобразование строки в двойное значение запрещено C #, поэтому вы получаете исключение, вам необходимо преобразовать строку ( документ MSDN, в котором показаны допустимые пути преобразования). Это просто потому, что строка не обязательно будет содержать числовые данные, но различные числовые типы будут (за исключением нулевых значений). A
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
нечисловая строкой.источник
TryParse
овер,Convert
потому чтоTryParse
проверяет успешность преобразования?double varDouble = (double)variable
предполагает, чтоvariable
это уже дубль. Еслиvariable
это не двойник (это строка), тогда это не удастся.double varDouble = Convert.ToDouble(variable)
неужели как говорится - конвертирует. Если он может проанализировать или иным образом извлечь изvariable
него двойник, он это сделает.Я использую
Double.Parse
илиDouble.TryParse
потому что он более четко указывает на то, что должно происходить. Вы начинаете со строки и ожидаете, что она будет преобразована в двойную. Если есть сомнения, используйтеTryParse
.Если
variable
это аргумент метода, измените тип на double. Сделайте вызывающего ответственным за предоставление правильного типа. Таким образом, компилятор сделает всю работу за вас.источник
Наиболее важное различие состоит в том, что если используется приведение типа и преобразование завершается неудачно (скажем, мы преобразуем очень большое значение с плавающей запятой в int), исключение не генерируется и отображается минимальное значение, которое может содержать int. Но в случае использования Convert для таких сценариев будет выброшено исключение.
источник