Если установлено, что просто проверка navigator.userAgent
не всегда надежна. Большая надежность может быть достигнута также путем проверки navigator.platform
. Простая модификация предыдущего ответа, кажется, работает лучше:
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent) ||
(/Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.platform))) {
// some code...
}
ОБНОВЛЕНИЕ: Я использовал этот вопрос как основу для записи в блоге, здесь:
См. комментарии в блоге для более подробного обсуждения этой проблемы. Спасибо за отличный вопрос!
Вы наткнулись на интересное и досадное несоответствие между системой типов CLI и системой типов C #.
В CLI есть концепция «совместимости по назначению». Если значение x известного типа данных S является «совместимым по назначению» с конкретным местом хранения y известного типа данных T, то вы можете сохранить x в y. Если нет, то такой код не поддается проверке, и проверяющий его запретит.
Система типов CLI говорит, например, что подтипы ссылочного типа совместимы по присваиванию с супертипами ссылочного типа. Если у вас есть строка, вы можете сохранить ее в переменной типа object, потому что оба являются ссылочными типами, а строка - подтипом объекта. Но обратное неверно; супертипы несовместимы с подтипами по присваиванию. Вы не можете вставить что-то, что только известно как объект, в переменную типа string без предварительного преобразования.
В основном «совместимость с присваиванием» означает «имеет смысл вставить именно эти биты в эту переменную». Присваивание исходного значения целевой переменной должно «сохранять представление». Подробности см. В моей статье:
http://ericlippert.com/2009/03/03/presentation-and-identity/
Одно из правил интерфейса командной строки гласит: «Если X является присваиванием, совместимым с Y ,
Но это законно в CLI. Итак, теперь мы стоим перед выбором.
1) Реализовать "is", чтобы, когда компилятор не может определить ответ статически, он фактически вызывает метод, который проверяет все правила C # на возможность преобразования с сохранением идентичности. Это медленно, и в 99,9% случаев соответствует правилам CLR. Но мы принимаем удар по производительности, чтобы быть на 100% совместимым с правилами C #.
2) Реализуем "is", чтобы, когда компилятор не может определить ответ статически, он выполнял невероятно быструю проверку совместимости назначения CLR и смиритесь с тем фактом, что здесь говорится, что uint [] является int [], хотя на самом деле это не было бы законным в C #.
Мы выбрали последнее. К сожалению, спецификации C # и CLI расходятся во мнениях по этому второстепенному вопросу, но мы готовы мириться с несогласованностью.
Провести фрагмент через Reflector:
sbyte[] foo = new sbyte[10];
object bar = foo;
Console.WriteLine("{0} {1} {2} {3}", new object[] { foo != null, false, bar is sbyte[], bar is byte[] });
Компилятор C # оптимизирует первые два сравнения ( foo is sbyte []
и foo is byte []
). Как видите, они оптимизированы для foo! = Null
и просто всегда false
.
Также интересно:
sbyte[] foo = new sbyte[] { -1 };
var x = foo as byte[]; // doesn't compile
object bar = foo;
var f = bar as byte[]; // succeeds
var g = f[0]; // g = 255
Несомненно, вывод правильный. bar "равно" sbyte [] и byte [], потому что он совместим с обоими, поскольку bar является просто объектом, тогда он "может быть" либо со знаком, либо без знака.
"is" определяется как "выражение может быть приведение к типу ".