При программировании интерфейсов, я обнаружил, что я часто использую приведение типов или преобразование типов объектов.
Есть ли разница между этими двумя методами конвертации? Если да, то есть ли разница в стоимости или как это повлияет на мою программу?
public interface IMyInterface
{
void AMethod();
}
public class MyClass : IMyInterface
{
public void AMethod()
{
//Do work
}
// Other helper methods....
}
public class Implementation
{
IMyInterface _MyObj;
MyClass _myCls1;
MyClass _myCls2;
public Implementation()
{
_MyObj = new MyClass();
// What is the difference here:
_myCls1 = (MyClass)_MyObj;
_myCls2 = (_MyObj as MyClass);
}
}
Кроме того, что является "вообще" предпочтительным методом?
Ответы:
Ответ под строкой был написан в 2008 году.
В C # 7 введено сопоставление с образцом, которое в значительной степени заменило
as
оператор, теперь вы можете написать:Обратите внимание, что
tt
после этого он все еще находится в области действия, но не определенно назначен. (Это является определенно присвоенной вif
теле). Это немного раздражает в некоторых случаях, так что если вы действительно заботитесь о введении наименьшее число переменных , возможных в любой области видимости, вы все равно можете использовать сis
последующим броском.Я не думаю, что какие-либо ответы до сих пор (во время запуска этого ответа!) Действительно объяснили, где и какой стоит использовать.
Не делай этого:
Это не только проверка дважды, но и проверка разных вещей, если
randomObject
это поле, а не локальная переменная. Возможно, что «если» пройдет, но приведение не будет выполнено, если другой поток изменит значениеrandomObject
между ними.Если
randomObject
действительно должен быть экземплярTargetType
, то есть, если это не так, это означает, что есть ошибка, тогда приведение является правильным решением. Это немедленно вызывает исключение, что означает, что больше нет работы при неправильных предположениях, и исключение правильно показывает тип ошибки.Если
randomObject
может быть экземпляромTargetType
иTargetType
является ссылочным типом, используйте код, подобный следующему:Если
randomObject
может быть экземпляромTargetType
иTargetType
является типом значения, то мы не можем использоватьas
сTargetType
самим собой, но мы можем использовать обнуляемый тип:(Примечание: в настоящее время это на самом деле медленнее, чем + + cast . Я думаю, что это более элегантно и последовательно, но мы здесь.)
Если вам действительно не нужно преобразованное значение, но вам просто нужно знать, является ли оно экземпляром TargetType, тогда
is
оператор - ваш друг. В этом случае не имеет значения, является ли TargetType ссылочным типом или типом значения.Могут быть и другие случаи использования дженериков, где
is
это полезно (потому что вы можете не знать, является ли T ссылочным типом или нет, поэтому вы не можете использовать как), но они относительно неясны.Я почти наверняка использовал
is
для случая значения типа до сих пор, не думая об использовании обнуляемого типа иas
вместе :)РЕДАКТИРОВАТЬ: Обратите внимание, что ни один из вышеперечисленных не говорит о производительности, кроме случая типа значения, где я заметил, что распаковка в тип значения, допускающий значение NULL, на самом деле медленнее - но согласованно.
В соответствии с ответом Нааскинга, is-and-cast или is-and-as являются такими же быстрыми, как и нулевая проверка с современными JIT, как показано кодом ниже:
На моем ноутбуке все это выполняется примерно за 60 мс. Обратите внимание на две вещи:
Так что давайте не будем беспокоиться о производительности. Давайте позаботимся о правильности и последовательности.
Я утверждаю, что is-and-cast (или is-and-as) небезопасны при работе с переменными, так как тип значения, на которое он ссылается, может измениться из-за другого потока между тестом и приведением. Это было бы довольно редкой ситуацией - но я бы предпочел соглашение, которое я могу использовать последовательно.
Я также утверждаю, что проверка «как тогда ноль» дает лучшее разделение проблем. У нас есть одно утверждение, которое пытается преобразовать, а затем одно утверждение, которое использует результат. Метод is-and-cast или is-and-as выполняет тест, а затем еще одну попытку преобразовать значение.
Другими словами, кто- нибудь написал бы:
Это своего рода то, что делает актерский состав - хотя, очевидно, более дешевым способом.
источник
if (randomObject is TargetType convertedRandomObject){ // Do stuff with convertedRandomObject.Value}
или использоватьswitch
/case
смотреть документы«as» вернет NULL, если невозможно разыграть.
кастинг до поднимет исключение.
Для производительности повышение исключения обычно более затратно во времени.
источник
Вот еще один ответ, с некоторым сравнением IL. Рассмотрим класс:
Теперь посмотрим на IL, который выдает каждый метод. Даже если операционные коды ничего не значат для вас, вы можете увидеть одно существенное отличие - вызывается isinst, за которым следует castclass в методе DirectCast. Так что два звонка вместо одного в принципе.
Ключевое слово isinst по сравнению с кастклассом
В этом посте есть достойное сравнение двух способов сделать это. Его резюме:
Лично я всегда использую как, потому что это легко читать и рекомендуется командой разработчиков .NET (или Джеффри Рихтер в любом случае)
источник
Одно из более тонких различий между ними заключается в том, что ключевое слово «as» нельзя использовать для приведения, когда задействован оператор приведения:
Это не скомпилирует (хотя я думаю, что это было в предыдущих версиях) в последней строке, так как ключевые слова «как» не принимают во внимание операторы приведения. Линия
string cast = (string)f;
работает просто отлично, хотя.источник
as никогда не выдает исключение, если не может выполнить преобразование, возвращающее ноль вместо этого ( поскольку работает только со ссылочными типами). Таким образом, использование как в основном эквивалентно
С другой стороны, приведения в стиле C выдает исключение, когда преобразование невозможно.
источник
Не совсем ответ на ваш вопрос, но я думаю, что это важный связанный вопрос.
Если вы программируете на интерфейс, вам не нужно создавать каст. Надеюсь, эти броски очень редки. Если нет, то вам, вероятно, нужно переосмыслить некоторые из ваших интерфейсов.
источник
Пожалуйста, не обращайте внимания на советы Джона Скита: избегайте шаблонов «тест-и-бросок», т.е.
Идея, что это стоит больше, чем приведение и нулевой тест, является МИФОМ :
Это микрооптимизация, которая не работает. Я выполнил несколько реальных тестов , и тестирование-и-приведение на самом деле быстрее, чем сравнение-и-ноль-сравнение, и это также безопаснее, потому что у вас нет возможности иметь нулевую ссылку в области действия вне приведения. потерпеть поражение.
Если вам нужна причина, почему тестирование и приведение происходит быстрее или, по крайней мере, не медленнее, есть простая и сложная причина.
Все просто: даже наивные компиляторы объединят две одинаковые операции, такие как test-and-cast, в один тест и ветвь. cast-and-null-test может вызвать два теста и ветвь, один для проверки типа и преобразования в null при ошибке, один для самой проверки null. По крайней мере, они оба будут оптимизированы для одного теста и ветвления, поэтому тестирование и приведение не будут ни медленнее, ни быстрее, чем приведение и приведение к нулю.
Сложно: почему тестирование и приведение происходит быстрее: метод «бросить и обнулить» вводит во внешнюю область видимости другую переменную, которую компилятор должен отслеживать для обеспечения жизнеспособности, и он может не иметь возможности оптимизировать эту переменную в зависимости от сложности вашего управления. поток есть. И наоборот, тестирование и приведение вводят новую переменную только в области с разделителями, поэтому компилятор знает, что переменная устарела после выхода из области, и поэтому может лучше оптимизировать распределение регистров.
Поэтому, пожалуйста, ПОЖАЛУЙСТА, пусть этот «бросок-и-ноль-проверка лучше, чем проверка-и-бросок» совет DIE. ПОЖАЛУЙСТА. Тестирование и приведение более безопасны и быстрее.
источник
ref
параметр. Это безопасно для локальных переменных, но не для полей. Мне было бы интересно запустить ваши тесты, но код, который вы дали в своем блоге, неполон. Я согласен с не микрооптимизацией, но я не думаю, что использование значения дважды более удобочитаемо или элегантно, чем использование «как» и тест на недействительность. (Между прочим, я бы определенно использовал прямой бросок, а не «как» после того, как есть.)Если приведение не выполнено, ключевое слово as не вызывает исключение; вместо этого он устанавливает переменную в null (или в значение по умолчанию для типов значений).
источник
Это не ответ на вопрос, а комментарий к примеру кода вопроса:
Обычно вам не нужно приводить объект из, например, IMyInterface в MyClass. Самое замечательное в интерфейсах состоит в том, что если вы берете объект в качестве входных данных, который реализует интерфейс, то вам не нужно заботиться о том, какой объект вы получаете.
Если вы приведете IMyInterface к MyClass, то вы уже предполагаете, что получаете объект типа MyClass, и нет смысла использовать IMyInterface, потому что если вы передадите свой код другим классам, которые реализуют IMyInterface, это нарушит ваш код ...
Теперь мой совет: если ваши интерфейсы хорошо спроектированы, вы можете избежать большого количества типов.
источник
as
Оператор может быть использован только на ссылочные типы, не могут быть перегружены, и он вернется ,null
если операция не выполняется. Это никогда не бросит исключение.Приведение может использоваться для любых совместимых типов, оно может быть перегружено, и оно выдаст исключение в случае сбоя операции.
Выбор того, что использовать, зависит от обстоятельств. Прежде всего, это вопрос того, хотите ли вы выдать исключение при неудачном преобразовании.
источник
Мой ответ касается только скорости в тех случаях, когда мы не проверяем тип и не проверяем нули после приведения. Я добавил два дополнительных теста в код Джона Скита:
Результат:
Не пытайтесь сосредоточиться на скорости (как я), потому что все это очень, очень быстро.
источник
as
преобразование (без проверки ошибок) выполняется примерно на 1-3% быстрее, чем приведение (около 540 мс против 550 мс на 100 млн итераций). Ни один не сделает или сломает ваше заявление.Помимо всего того, что уже было здесь раскрыто, я натолкнулся на практическую разницу, которую, я думаю, стоит отметить, между явным приведением
по сравнению с использованием
as
оператора.Вот пример:
Итог: GenericCaster2 не будет работать с типами структуры. GenericCaster будет.
источник
Если вы используете Office PIA для .NET Framework 4.X, вам следует использовать ключевое слово as , иначе оно не будет компилироваться.
Кастинг в порядке при нацеливании на .NET 2.0, хотя:
При нацеливании на .NET 4.X возникают следующие ошибки:
ошибка CS0656: отсутствует требуемый для компилятора элемент 'Microsoft.CSharp.RuntimeBinder.Binder.Convert'
ошибка CS0656: отсутствует требуемый компилятор элемент 'Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create'
источник
as
Ключевое слово работает так же , как явное приведение между совместимыми ссылочными типами с основным отличием , что он не вызывает исключение , если преобразование не удалось. Скорее, он возвращает нулевое значение в целевой переменной. Поскольку исключения очень дороги с точки зрения производительности, это считается гораздо лучшим методом приведения.источник
То, что вы выбираете, сильно зависит от того, что требуется. Я предпочитаю явное приведение
потому что если объект должен иметь тип IMyInterface, а это не так - это определенно проблема. Лучше получить ошибку как можно раньше, потому что точная ошибка будет исправлена, а не устранение ее побочного эффекта.
Но если вы имеете дело с методами, которые принимают в
object
качестве параметра, вам нужно проверить его точный тип перед выполнением любого кода. В таком случаеas
было бы полезно, чтобы вы могли избежатьInvalidCastException
.источник
Это зависит от того, хотите ли вы проверить на null после использования «как» или вы бы предпочли, чтобы ваше приложение выдало исключение?
Мое эмпирическое правило: если я всегда ожидаю, что переменная будет того типа, которого я ожидаю в то время, когда я хочу, я использую приведение. Если возможно, что переменная не будет приведена к тому, что я хочу, и я готов обработать пустые значения от использования как, я буду использовать как.
источник
Посмотрите на эти ссылки:
они показывают вам некоторые детали и тесты производительности.
источник
Проблема ОП ограничена конкретной ситуацией. Название охватывает гораздо больше ситуаций.
Вот краткий обзор всех соответствующих ситуаций приведения, о которых я сейчас могу думать:
источник