Общий шаблон нулевого объекта в С#
Мне интересно, существует ли какой-либо подход для реализации шаблона нулевого объекта в С#. Общий нулевой объект является подклассом всех ссылочных типов, как и Nothing
в Scala. Кажется,
public class Nothing<T> : T where T : class
Но он не может скомпилировать, и я понятия не имею , как реализовать методы T
для обеспечения поведения по умолчанию или исключения исключений. Вот несколько соображений:
- Использовать отражение?
- Использовать дерево выражений при создании
Nothing<T>
? Возможно, это похоже на Moq. И еще вопрос: можно ли использовать mock framework/library в кодах продуктов?
- Использовать динамические типы?
Я ЗНАЮ, возможно, я должен реализовать конкретный нулевой объект для определенного типа. Мне просто интересно узнать, есть ли какие-либо решения.
Любое предложение? Спасибо.
Ответы
Ответ 1
С помощью дженериков вы не можете определить наследование от T
. Если вы намерены использовать if(x is Nothing<Foo>)
, то это просто не сработает. Не в последнюю очередь, вам нужно будет подумать об абстрактных типах, опечатанных типах и конструкторах, не относящихся к умолчанию. Однако вы можете сделать что-то вроде:
public class Nothing<T> where T : class, new()
{
public static readonly T Instance = new T();
}
Однако IMO, которая не выполняет большинство ключевых функций нулевого объекта; в частности, вы могли бы легко в конечном итоге с кем-то сделать:
Nothing<Foo>.Instance.SomeProp = "abc";
(возможно, случайно, после прохода объекта 3 уровня вниз)
Честно говоря, я думаю, вам стоит просто проверить null
.
Ответ 2
Как насчет этого?
public class Nothing<T> where T : class
{
public static implicit operator T(Nothing<T> nothing)
{
// your logic here
}
}
Ответ 3
Так как есть запечатанные классы, вы не можете сделать такое наследование в родовом случае. Ожидается, что многие классы не будут получены, поэтому может быть неплохо, если бы это сработало.
Использование неявного оператора, предложенного @abatishchev, звучит как возможный подход.
Ответ 4
Я использую что-то подобное в своих проектах:
public interface IOptional<T> : IEnumerable<T> { }
public interface IMandatory<T> : IEnumerable<T> { }
Два интерфейса, полученных из IEnumerable для совместимости с LINQ
public class Some<T> : IOptional<T>
{
private readonly IEnumerable<T> _element;
public Some(T element)
: this(new T[1] { element })
{
}
public Some()
: this(new T[0])
{}
private Some(T[] element)
{
_element = element;
}
public IEnumerator<T> GetEnumerator()
{
return _element.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public class Just<T> : IMandatory<T>
{
private readonly T _element;
public Just(T element)
{
_element = element;
}
public IEnumerator<T> GetEnumerator()
{
yield return _element;
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Реализация классов Just и Some
Примечание: реализация этих классов очень похожа, но имеет одно отличие. Класс Просто получен из интерфейса IMandatory и имеет только один конструктор, который гарантирует, что экземпляр класса Just всегда имеет значение внутри.
public static class LinqExtensions
{
public static IMandatory<TOutput> Match<TInput, TOutput>(
this IEnumerable<TInput> maybe,
Func<TInput, TOutput> some, Func<TOutput> nothing)
{
if (maybe.Any())
{
return new Just<TOutput>(
some(
maybe.First()
)
);
}
else
{
return new Just<TOutput>(
nothing()
);
}
}
public static T Fold<T>(this IMandatory<T> maybe)
{
return maybe.First();
}
}
Некоторые расширения для практичности
Примечание: метод расширения Сопоставьте две функции и верните IMandatory, после этого метод расширения Fold используйте .First() без каких-либо проверок.
Теперь мы можем использовать полную мощность LINQ и писать код, подобный этому (я имею в виду monads.SelectMany())
var five = new Just<int>(5);
var @null = new Some<int>();
Console.WriteLine(
five
.SelectMany(f => @null.Select(n => f * n))
.Match(
some: r => $"Result: {r}",
nothing: () => "Ups"
)
.Fold()
);
Ответ 5
Как насчет уже существующей реализации Nullable в .NET Framework?
http://msdn.microsoft.com/en-us/library/b3h38hb0.aspx