C#.Net中的可选返回

Java 1.8正在接收Optional类,它允许我们明确说明方法何时可以返回null值并“强制”其使用者在使用它之前validation它是否为null( isPresent() )。

我看到C#有Nullable,它做的类似,但基本类型。 它似乎用于数据库查询,以区分值何时存在,并且当它不存在时为0并且为空。

但似乎C#的Nullable不适用于对象,仅适用于基本类型,而Java的Optional仅适用于对象而不适用于基本类型。

在C#中是否有Nullable / Optional类,这迫使我们在提取和使用它之前测试对象是否存在?

不是语言,不,但你可以自己做:

 public struct Optional { public bool HasValue { get; private set; } private T value; public T Value { get { if (HasValue) return value; else throw new InvalidOperationException(); } } public Optional(T value) { this.value = value; HasValue = true; } public static explicit operator T(Optional optional) { return optional.Value; } public static implicit operator Optional(T value) { return new Optional(value); } public override bool Equals(object obj) { if (obj is Optional) return this.Equals((Optional)obj); else return false; } public bool Equals(Optional other) { if (HasValue && other.HasValue) return object.Equals(value, other.value); else return HasValue == other.HasValue; } } 

请注意,您将无法模拟Nullable某些行为,例如将没有值的可空值设置为null的能力,而不是盒装的可空,因为它具有特殊的编译器支持(并且一些其他的)行为。

在我看来,暴露HasValue属性的任何Option实现都是整个想法的失败。 可选对象的要点是您可以无条件地调用其内容,而无需测试内容是否存在。

如果必须测试可选对象是否包含值,那么与常见的null测试相比,您没有做任何新的事情。

这篇文章中我详细解释了可选对象: 自定义实现选项/可能在C#中输入

这里是带有代码和示例的GitHub存储库: https : //github.com/zoran-horvat/option

如果您不愿意使用重量级的Option解决方案,那么您可以轻松构建轻量级的解决方案。 您可以创建自己的Option类型来实现IEnumerable接口,这样您就可以利用LINQ扩展方法来调用可选的调用。 这是最简单的实现:

 public class Option : IEnumerable { private readonly T[] data; private Option(T[] data) { this.data = data; } public static Option Create(T value) { return new Option(new T[] { value }); } public static Option CreateEmpty() { return new Option(new T[0]); } public IEnumerator GetEnumerator() { return ((IEnumerable)this.data).GetEnumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return this.data.GetEnumerator(); } } 

使用此Option类型通过LINQ完成:

 Option optional = Option.Create(myCar); string color = optional .Select(car => car.Color.Name) .DefaultIfEmpty(""); 

您可以在这些文章中找到有关可选对象的更多信息

  • 在C#中自定义实现Option / Maybe类型
  • 理解选项(可能)function类型
  • 如何降低圈复杂度:期权function类型

您可以参考我的video课程,了解有关如何使用Option类型和其他方法简化控制流程的更多详细信息: 在.NET中 制作C#代码更多function和战术设计模式:控制流程

第一个video课程( 让您的C#代码更具function性 )详细介绍了面向铁路的编程,包括EitherOption类型,以及它们如何用于管理可选对象和处理特殊情况和错误。

在C#中有更好的选项类型实现。 您可以通过pluralsight.com上的Zoran Horvat在.NET中的战术设计模式中找到这种实现方式 。 它包括解释为什么以及如何使用它。 基本思想是将选项类实现为IEnumerable <>接口的实现。

 public class Option : IEnumerable { private readonly T[] data; private Option(T[] data) { this.data = data; } public static Option Create(T element) { return new Option(new[] { element }); } public static Option CreateEmpty() { return new Option(new T[0]); } public IEnumerator GetEnumerator() { return ((IEnumerable) this.data).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } } 

内置任何东西,但您可以定义自己的内置。 请注意,如果没有定义map / bind运算符, Option实现就没有意义。

 public struct Option { private bool hasValue; private T value; public Option(T value) { if (value == null) throw new ArgumentNullException("value"); this.hasValue = true; this.value = value; } public Option Select(Func selector) { return this.hasValue ? new Option(selector(this.value)) : new Option(); } public Option SelectMany(Func> bind) { return this.hasValue ? bind(this.value) : new Option(); } public bool HasValue { get { return this.hasValue; } } public T GetOr(T @default) { return this.hasValue ? this.value : @default; } } 

也许这更接近F#Option类型

 public struct Option { private T value; private readonly bool hasValue; public bool IsSome => hasValue; public bool IsNone => !hasValue; public T Value { get { if (!hasValue) throw new NullReferenceException(); return value; } } public static Option None => new Option(); public static Option Some(T value) => new Option(value); private Option(T value) { this.value = value; hasValue = true; } public TResult Match(Func someFunc, Func noneFunc) => hasValue ? someFunc(value) : noneFunc(); public override bool Equals(object obj) { if (obj is Option) { var opt = (Option)obj; return hasValue ? opt.IsSome && opt.Value.Equals(value) : opt.IsNone; } return false; } public override int GetHashCode() => hasValue ? value.GetHashCode() : 0; } 

在C#中是否有Nullable / Optional类,这迫使我们在提取和使用它之前测试对象是否存在?

创建了Nullables,以便原始类型可以为null。 它们的默认值不一定是实际值(如int,没有nullables它的默认值是0,那么0表示0或不设置为0?)

没有什么可以强制程序员检查对象是否为空。 那很好。 这样做会产生巨大的开销。 如果这是一种语言function,您多久会强行检查一次? 首次分配变量时是否需要它? 如果变量稍后指向另一个对象怎么办? 你会强制它在每个方法和属性之前检查,如果失败你会抛出exception吗? 你现在可以使用null引用exception。 除了你已经拥有的东西之外,强迫某人做这件事你会得到很少的好处。

您可以使用FSharpCore.dll程序FSharpCore.dll Microsoft.FSharp.Core.FSharpOption ,而不是编写自己的类。 不幸的是,在C#中使用时,F#类型有点笨拙。

 //Create var none = FSharpOption.None; var some1 = FSharpOption.Some("some1"); var some2 = new FSharpOption("some2"); //Does it have value? var isNone1 = FSharpOption.get_IsNone(none); var isNone2 = OptionModule.IsNone(none); var isNone3 = FSharpOption.GetTag(none) == FSharpOption.Tags.None; var isSome1 = FSharpOption.get_IsSome(some1); var isSome2 = OptionModule.IsSome(some1); var isSome3 = FSharpOption.GetTag(some2) == FSharpOption.Tags.Some; //Access value var value1 = some1.Value; //NullReferenceException when None var value2 = OptionModule.GetValue(some1); //ArgumentException when None 

我决定前一段时间使用最后一个C#版本实现某种Optional <> Java类原型。

事实上这是:

 public sealed class Optional { private static readonly Optional EMPTY = new Optional(); private readonly T value; private Optional() => value = default; private Optional(T arg) => value = arg.RequireNonNull("Value should be presented"); public static Optional Empty() => EMPTY; public static Optional Of(T arg) => new Optional(arg); public static Optional OfNullable(T arg) => arg != null ? Of(arg) : Empty(); public static Optional OfNullable(Func outputArg) => outputArg != null ? Of(outputArg()) : Empty(); public bool HasValue => value != null; public void ForValuePresented(Action action) => action.RequireNonNull()(value); public IOption Where(Predicate predicate) => HasValue ? predicate.RequireNonNull()(value) ? this : Empty() : this; public IOption Select(Func select) => HasValue ? Optional.OfNullable(select.RequireNonNull()(value)) : Optional.Empty(); public IOption> SelectMany(Func> select) => HasValue ? Optional>.OfNullable(select.RequireNonNull()(value)) : Optional>.Empty(); public T Get() => value; public T GetCustomized(Func getCustomized) => getCustomized.RequireNonNull()(value); public U GetCustomized(Func getCustomized) => getCustomized.RequireNonNull()(value); public T OrElse(T other) => HasValue ? value : other; public T OrElseGet(Func getOther) => HasValue ? value : getOther(); public T OrElseThrow(Func exceptionSupplier) where E : Exception => HasValue ? value : throw exceptionSupplier(); public static explicit operator T(Optional optional) => OfNullable((T) optional).Get(); public static implicit operator Optional(T optional) => OfNullable(optional); public override bool Equals(object obj) { if (obj is Optional) return true; if (!(obj is Optional)) return false; return Equals(value, (obj as Optional).value); } public override int GetHashCode() => base.GetHashCode(); public override string ToString() => HasValue ? $"Optional has <{value}>" : $"Optional has no any value: <{value}>"; 

}