> Вот представьте, есть у вас маленький ребенок (а у некоторых он действительно есть). И спрашивает у вас ребенок типичный детский вопрос «Почему трава зеленая».
> Хлорофилл нужен, чтобы обеспечивать процесс фотосинтеза
> Трава зеленая, потому что при таком цвете она может получать больше тепла от солнышка и лучше расти.
> А как же RAII? Видимо, я что-то не понимаю в концепции языка.
Аналогом плюсового деструктора в C# является метод `Dispose()` интерфейса `IDisposable` (не путать с тильдой-финализатором). RAII в C# реализуется через паттерн Disposable. Аналогом плюсового локального скоупа является синтаксис `using (var foo = new SomeDisposable()) {… }` — метод `foo.Dispose()` вызовется при покидании скоупа при достижении конца, `return` или исключении.
> Кто контролирует отписку?
Для контролируемой отписки а-ля RAII как в boost::signals2 проще использовать не стандартные события, а Rx, см. мой комментарий выше.
> В каких случаях чаще всего используются события с C#?
Сырые события .NET удобно использовать в ситуациях, когда время жизни подписчика заведомо не меньше времени жизни источника событий, тогда отписка не нужна. При желании можно вручную сконструировать «токен подписки», `Dispose()` которого вызовет отписку:
EventHandler<MyArgs> handler = (sender, e) => { Debug.Log(“Some message.”); subscriber.DoSomething(e.SomeProperty, e.AnotherProperty); }
publisher.SomeEvent += handler;
// Замыкаем подписчика и источник событий в безликом `Action`:
Action actionToBeCalledOnDispose = () => { publisher.SomeEvent -= handler; }
_subscription = Disposable.Create(actionToBeCalledOnDispose);
> не возможно не зная пароля на сервере поддерживать все защищённые методы аутентификации
Все и не надо.
>Пароли просто обязаны хранится в открытом виде… Там на вход нужно подавать пароль в открытом виде.
Необходимость подавать пароль в открытом виде не означает необходимости хранить пароль в открытом виде в базе. Сервер получает пароль через TLS, считает от него HMAC с солью, забывает пароль, сравнивает HMAC с хранящимся в базе.
Не знаю ни одного примера игр для iOS или Android, в которых таблицы лидеров не были бы взломаны.
В настройках гугловских лидербордов можно опционально указать пределы для количества очков, чтоб отсеять заведомо накрученные значения, а также (как я понял) можно вести список подозрительных игроков, которые будут спрятаны (не будут отображаться в лидерборде):
«Limits are optional values that define the lower and upper limits of scores that are allowed in the leaderboard. This can help you discard score submissions that are clearly fraudulent. You can also use Players.hide to hide players that you believe have submitted fraudulent scores from all leaderboards in your app.»
Можно добавить и Braid: «Несмотря на жёсткие, в общем, требования Microsoft к играм под Xbox, группа сертификации пошла на уступки Блоу: запустив Braid, игрок попадал прямо в игру (а не в главное меню).»
Там же можно найти url для iframe, в котором можно узнать путь до .unity3d файла.
Что-то не получается найти. Подскажи, пожалуйста, как извлечь ссылку, по которой доступен .unity3d-файл (возможно, с другим расширением), и где браузер его кеширует.
Если API гарантирует, что CanUserId(id) достаточно для последующего бессбойного вызова GetUser(id), то можно кидать UsageException (в случае C# это производное от ArgumentException). То есть трактовать ошибку можно как нарушение контракта — пользователь не сделал проверку существования перед обращением. Не вижу никаких противоречий.
Но важно то, что без дополнительных действий (например, блокировки) состояние словаря может измениться между проверкой и получением значения, как следствие, не вполне очевидно, это ошибка времени разработки или времени выполнения.
Это ошибка времени разработки, так как это нарушение контракта — в упомянутом разделе Thread Safety сказано, что Dictionary не гарантирует корректную работу без внешней синхронизации. Обрабатываться эта ошибка должна не динамически (ловим KeyNotFoundException), а статически (меняем код так, чтоб не было неправомерного использования в многопоточной ситуации, для которой класс не предназначен; добавляем проверки — TryGetValue или if).
корректно ли кидать ArgumentOutOfRangeException при попытке достать несуществующий (находящийся за пределами строки) символ. Почему тогда некорректно кидать его же при попытке достать несуществующего пользователя?
Находится ли символ за пределами строки (или ключ в словаре, etc) — может быть проверено не прибегая к механизму исключений, просто if'ом. Ошибка несуществующего элемента в этом случае — ошибка времени разработки, так как разработчик забыл сделать проверку.
Напротив, существование пользователя (или файла на диске, etc) в общем случае не может быть проверено как «if (UserExists(...))», потому что между проверкой и обращением ситуация может измениться (обновиться база данных, или файловая система). В этом случае отсутствие пользователя является ошибкой времени выполнения, вполне объективная исключительная ситуация, которая не может быть обработана статически (изменением кода вызова).
По поводу упоминающегося в комментариях ArgumentException. Это исключение (и все производные от него) соверешенно особенное, оно означает нарушение контракта. Оно никогда не должно возникать при выполнении программ. Его возникновение должно быть 100% предсказуемо до вызова метода, на основании значений параметров и свойств объекта согласно контракту метода. Это исключение означает неправильно написанную программу.
В частности, ArgumentException (будучи design error) не должен ловиться пользователем библиотеки; при возбуждении этого исключения нужно менять вызывающий код, добавить какую-то проверку.
Да не важно, x2, или x1.95, или x2.05. Важно, что размеры некоторых (но не всех) элементов интерфейса завязаны на внешние физические параметры: длина большого пальца, площадь касания. И НЕ завязаны на размер экрана, разрешение, dpi, форм-фактор, пропорции, операционную систему, модель, etc.
Скейл зависит от РАЗРЕШЕНИЯ, а не от размера девайса.
Предположим, на смартфоне кнопка имеет видимую площадь (условно) 2 cm^2, на расстоянии 1 cm от края экрана. Теперь возьмём планшет с тем же разрешением, но вдвое большими размерностями (соответственно, вдвое меньшим dpi). Формально никаких апскейлов не будет, но визуально интерфейс увеличится в два раза. И теперь копка будет иметь конский размер 8 cm^2 (и прячет под собой большой кусок потенциально видимого контента), и находится на расстоянии 2 cm от края (тяжелей дотягиваться фалангой пальца, держа девайс в руках). Я правильно понял?
А на гифке выделены ПОН, СРД, ЧТВ.
«Если маленький ребенок когда-нибудь спросит вас почему небо голубое, лучше, если вы посмотрите ему прямо в глаза и скажете: „Оно голубое из-за квантовых эффектов в рэлеевском рассеянии вместе с недостатком приемников фиолетового цвета в нашей сетчатке“» © Фил Плейт goo.gl/KgSe3B
> Трава зеленая, потому что при таком цвете она может получать больше тепла от солнышка и лучше расти.
Это попахивает телеологией.
Аналогом плюсового деструктора в C# является метод `Dispose()` интерфейса `IDisposable` (не путать с тильдой-финализатором). RAII в C# реализуется через паттерн Disposable. Аналогом плюсового локального скоупа является синтаксис `using (var foo = new SomeDisposable()) {… }` — метод `foo.Dispose()` вызовется при покидании скоупа при достижении конца, `return` или исключении.
> Кто контролирует отписку?
Для контролируемой отписки а-ля RAII как в boost::signals2 проще использовать не стандартные события, а Rx, см. мой комментарий выше.
> В каких случаях чаще всего используются события с C#?
Сырые события .NET удобно использовать в ситуациях, когда время жизни подписчика заведомо не меньше времени жизни источника событий, тогда отписка не нужна. При желании можно вручную сконструировать «токен подписки», `Dispose()` которого вызовет отписку:
EventHandler<MyArgs> handler = (sender, e) => { Debug.Log(“Some message.”); subscriber.DoSomething(e.SomeProperty, e.AnotherProperty); }
publisher.SomeEvent += handler;
// Замыкаем подписчика и источник событий в безликом `Action`:
Action actionToBeCalledOnDispose = () => { publisher.SomeEvent -= handler; }
_subscription = Disposable.Create(actionToBeCalledOnDispose);
Все и не надо.
>Пароли просто обязаны хранится в открытом виде… Там на вход нужно подавать пароль в открытом виде.
Необходимость подавать пароль в открытом виде не означает необходимости хранить пароль в открытом виде в базе. Сервер получает пароль через TLS, считает от него HMAC с солью, забывает пароль, сравнивает HMAC с хранящимся в базе.
В настройках гугловских лидербордов можно опционально указать пределы для количества очков, чтоб отсеять заведомо накрученные значения, а также (как я понял) можно вести список подозрительных игроков, которые будут спрятаны (не будут отображаться в лидерборде):
«Limits are optional values that define the lower and upper limits of scores that are allowed in the leaderboard. This can help you discard score submissions that are clearly fraudulent. You can also use Players.hide to hide players that you believe have submitted fraudulent scores from all leaderboards in your app.»
Мне понравилась связка JustDecompile + Reflexil. Та можно менять C#-код.
Что-то не получается найти. Подскажи, пожалуйста, как извлечь ссылку, по которой доступен .unity3d-файл (возможно, с другим расширением), и где браузер его кеширует.
Это ошибка времени разработки, так как это нарушение контракта — в упомянутом разделе Thread Safety сказано, что Dictionary не гарантирует корректную работу без внешней синхронизации. Обрабатываться эта ошибка должна не динамически (ловим KeyNotFoundException), а статически (меняем код так, чтоб не было неправомерного использования в многопоточной ситуации, для которой класс не предназначен; добавляем проверки — TryGetValue или if).
На эту тему надо смотреть раздел Thread Safety в статье MSDN Library, посвящённой классу словаря (Dictionary, ConcurrentDictionary, etc).
Находится ли символ за пределами строки (или ключ в словаре, etc) — может быть проверено не прибегая к механизму исключений, просто if'ом. Ошибка несуществующего элемента в этом случае — ошибка времени разработки, так как разработчик забыл сделать проверку.
Напротив, существование пользователя (или файла на диске, etc) в общем случае не может быть проверено как «if (UserExists(...))», потому что между проверкой и обращением ситуация может измениться (обновиться база данных, или файловая система). В этом случае отсутствие пользователя является ошибкой времени выполнения, вполне объективная исключительная ситуация, которая не может быть обработана статически (изменением кода вызова).
В частности, ArgumentException (будучи design error) не должен ловиться пользователем библиотеки; при возбуждении этого исключения нужно менять вызывающий код, добавить какую-то проверку.
Framework Design Guidelines дают однозначный ответ на этот вопрос: «DO NOT throw or derive from ApplicationException.»
Предположим, на смартфоне кнопка имеет видимую площадь (условно) 2 cm^2, на расстоянии 1 cm от края экрана. Теперь возьмём планшет с тем же разрешением, но вдвое большими размерностями (соответственно, вдвое меньшим dpi). Формально никаких апскейлов не будет, но визуально интерфейс увеличится в два раза. И теперь копка будет иметь конский размер 8 cm^2 (и прячет под собой большой кусок потенциально видимого контента), и находится на расстоянии 2 cm от края (тяжелей дотягиваться фалангой пальца, держа девайс в руках). Я правильно понял?