Objective-C имеет __attribute__((availability))
дольше, чем он имел @available
. Чтобы заставить это работать, компилятор Objective C слабо связывает символы, которые не доступны в цели развертывания. Это означает, что компиляция всегда завершается успешно, а запуск вашего приложения успешен, но символ будет НЕДЕЙСТВИТЕЛЕН во время выполнения, если он недоступен.
В зависимости от того, что это такое, вы получите более или менее изящную деградацию, когда попытаетесь ее использовать:
[ 1116] Старый способ проверить, найден ли символ во время выполнения, состоит в том, чтобы просто сравнить его с NULL:
NS_AVAILABLE_MAC(...) @interface Foo @end
int bar NS_AVAILABLE_MAC(...);
int baz(int frob) NS_AVAILABLE_MAC(...);
if ([Foo class]) { /* Foo is available */ }
if (&bar) { /* bar is available */ }
if (&baz) { /* baz is available */ }
В вашем случае:
Reader *tmp = [[Reader alloc] init];
tmp
будет [ 115], потому что это будет то же самое, что и [[nil alloc] init]
.
Директива @available
была добавлена относительно недавно в Objective-C. Теперь можно использовать @available
в Objective-C так же, как вы используете #available
в Swift. Однако, чтобы сохранить обратную совместимость, возможно, никогда не будет ошибкой во время компиляции (при уровнях ошибок по умолчанию) пытаться использовать символ, который может быть недоступен для цели развертывания в Objective-C.
Если все, что Вы хотите выполнить, должно переопределить значение по умолчанию (и возвратить новый объект), Вы можете делать так при помощи DefaultIfEmpty () Метод прежде, чем назвать SingleOrDefault (). Что-то как:
var client = db.Clients
.Where(c => c.Name == name)
.DefaultIfEmpty(new Client { Name = name })
.SingleOrDefault();
Действительно, Вы просто хотите использовать пустой оператор объединения здесь. Пример:
var client = db.Clients
.Where(c => c.Name == "Some Client")
.SingleOrDefault() ?? new Client();
Это возвратит любые возвраты SingleOrDefault, за исключением того, что, если пустой указатель возвратов SingleOrDefault, выражение возвратит новый Клиент () вместо этого.
Править: Как John Skeet указал, это решение не дифференцируется между ситуацией, где там не идет ни в какое сравнение, и пустой элемент найден, хотя ясно это - не обязательно проблема во многих случаях. Альтернатива должна создать дополнительный метод следующим образом.
public static T SingleOrNew<T>(this IEnumerable<T> query) where T : new()
{
try
{
return query.Single();
}
catch (InvalidOperationException)
{
return new T();
}
}
Я сказал бы, что это - вероятно, самое изящное решение, которое работает в общем случае.
Это сделало бы? РЕДАКТИРОВАНИЕ Оказывается этим?? не будет работать с универсальным типом.
public static class IEnumerableExtensions
{
public static T SingleOrNew<T>( this IEnumerable<T> query ) where T : new()
{
var value = query.SingleOrDefault();
if (value == null)
{
value = new T();
}
return value;
}
}
Кажется, что это могло быть сделано, да. Не может сказать, что я не забываю находиться в ситуации, где я использовал бы ее сам, но достаточно легко реализовать. Что-то вроде этого:
public static T SingleOrNew<T>(this IEnumerable<T> source) where T : new()
{
if (source == null)
{
throw new ArgumentNullException("source");
}
using (IEnumerator<T> iterator = source.GetEnumerator())
{
if (!iterator.MoveNext())
{
return new T();
}
T first = iterator.Current;
if (iterator.MoveNext())
{
throw new InvalidOperationException();
}
return first;
}
}
Я добавлю его к списку операторов для включения в MoreLinq...
Другой, более общий подход должен был бы предоставить делегату, который будет только оценен при необходимости (мне нужно лучшее имя здесь, по общему признанию):
public static T SingleOrSpecifiedDefault<T>(this IEnumerable<T> source,
Func<T> defaultSelector)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
if (defaultSelector == null)
{
throw new ArgumentNullException("defaultSelector");
}
using (IEnumerator<T> iterator = source.GetEnumerator())
{
if (!iterator.MoveNext())
{
return defaultSelector();
}
T first = iterator.Current;
if (iterator.MoveNext())
{
throw new InvalidOperationException();
}
return first;
}
}
Почему Вы не добавляете его с помощью Дополнительного метода затем? Кажется, что это было бы удобно.
var theClient =
Repository<Client>
.Entities()
.Where(t => t.ShouldBeHere())
.SingleOrDefault()
?? new Client { Name = "Howdy!" }
;