在C#.NET可选回报(Optional return in C#.Net)

2019-09-01 02:03发布

Java的1.8正在接收可选类,这使我们能够明确地说,当一个方法返回一个空值和“力”的消费者,以验证它是否不为空( isPresent()使用它之前)。

我看到C#有可空,即有类似的功能,但基本类型。 这似乎是用于DB查询,辨别当值存在,并且从当它不存在为0,为空。

但似乎C#的可空不为对象的工作,只为基本类型,而Java的可选仅适用于对象,而不是基本类型。

是否有一个可空/可选类在C#中,这迫使我们测试,如果对象提取和使用它之前存在?

Answer 1:

没有语言,没有,但你可以自己制作:

public struct Optional<T>
{
    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<T> optional)
    {
        return optional.Value;
    }
    public static implicit operator Optional<T>(T value)
    {
        return new Optional<T>(value);
    }

    public override bool Equals(object obj)
    {
        if (obj is Optional<T>)
            return this.Equals((Optional<T>)obj);
        else
            return false;
    }
    public bool Equals(Optional<T> other)
    {
        if (HasValue && other.HasValue)
            return object.Equals(value, other.value);
        else
            return HasValue == other.HasValue;
    }
}

请注意,您将无法仿效的某些行为Nullable<T>如框可为空值,没有值设置为null,而不是盒装空的,因为它有特殊的编译器支持的能力(和一些其他的)行为。



Answer 2:

在我看来,任何Option暴露实施HasValue属性是整个思想的失败。 可选对象的一点是,你可以让自己的内容无条件调用,无需测试的内容是否是存在的。

如果你有测试可选对象是否包含一个值,那么你就没有做什么新的比较常见的null测试。

这是在我完全详细解释可选对象的文章: 选项的定制实施/也许键入C#

这里是GitHub的库的代码和例子: https://github.com/zoran-horvat/option

如果你不愿意使用一个重量级选项的解决方案,那么你就可以轻松地构建一个轻量级的一个。 你可以让你自己Option<T>它实现类型IEnumerable<T>接口,让您可以充分利用LINQ扩展方法依次调用可选。 这可能是最简单的实现:

public class Option<T> : IEnumerable<T>
{
    private readonly T[] data;

    private Option(T[] data)
    {
        this.data = data;
    }

    public static Option<T> Create(T value)
    {
        return new Option<T>(new T[] { value });
    }

    public static Option<T> CreateEmpty()
    {
        return new Option<T>(new T[0]);
    }

    public IEnumerator<T> GetEnumerator()
    {
        return ((IEnumerable<T>)this.data).GetEnumerator();
    }

    System.Collections.IEnumerator
        System.Collections.IEnumerable.GetEnumerator()
    {
        return this.data.GetEnumerator();
    }
}

使用此Option<T>类型是通过LINQ完成:

Option<Car> optional = Option<Car>.Create(myCar);
string color = optional
  .Select(car => car.Color.Name)
  .DefaultIfEmpty("<no car>")
  .Single();  // you can call First(), too

你可以找到更多关于这些文章可选对象:

  • 选项的定制实施/也许键入C#
  • 了解选项(可能)功能型
  • 如何降低圈复杂度:选项功能型

你可以参考我的视频课程,了解如何使用简化的控制流程的详细信息Option类型和其他手段: 使你的C#代码更功能和在.NET战术设计模式:控制流

第一视频课程( 使你的C#代码更功能 )带来详细的介绍面向铁路编程,包括EitherOption类型,以及它们如何被用来管理可选对象和处理异常情况和错误。



Answer 3:

有更好的实现在C#选项类型。 你可以找到这个实行在.NET中的战术设计模式,由霍瓦特卓然在pluralsight.com。 它包括一个解释为何以及如何使用它。 其基本思路是实现选项类作为实施的IEnumerable <>接口。

public class Option<T> : IEnumerable<T>
{
    private readonly T[] data;

    private Option(T[] data)
    {
        this.data = data;
    }

    public static Option<T> Create(T element)
    {
        return new Option<T>(new[] { element });
    }

    public static Option<T> CreateEmpty()
    {
        return new Option<T>(new T[0]);
    }

    public IEnumerator<T> GetEnumerator()
    {
        return ((IEnumerable<T>) this.data).GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}


Answer 4:

在项目“C#语言功能的扩展名” https://github.com/louthy/language-ext存在的F#等功能patters选项对象



Answer 5:

没有什么内置的,但你可以自己定义。 请注意,一个Option<T>执行没有意义而不定义地图/绑定操作符。

public struct Option<T>
{
    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<TOut> Select<TOut>(Func<T, TOut> selector)
    {
        return this.hasValue ? new Option<TOut>(selector(this.value)) : new Option<TOut>();
    }

    public Option<TOut> SelectMany<TOut>(Func<T, Option<TOut>> bind)
    {
        return this.hasValue ? bind(this.value) : new Option<TOut>();
    }

    public bool HasValue
    {
        get { return this.hasValue; }
    }

    public T GetOr(T @default)
    {
        return this.hasValue ? this.value : @default;
    }
}


Answer 6:

也许这是更接近F#选项类型

public struct Option<T>
{
    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<T> None => new Option<T>();

    public static Option<T> Some(T value) => new Option<T>(value);

    private Option(T value)
    {
        this.value = value;
        hasValue = true;
    }

    public TResult Match<TResult>(Func<T, TResult> someFunc, Func<TResult> noneFunc) =>
        hasValue ? someFunc(value) : noneFunc();

    public override bool Equals(object obj)
    {
        if (obj is Option<T>)
        {
            var opt = (Option<T>)obj;
            return hasValue ? opt.IsSome && opt.Value.Equals(value) : opt.IsNone;
        }
        return false;
    }

    public override int GetHashCode() =>
        hasValue ? value.GetHashCode() : 0;
}


Answer 7:

是否有一个可空/可选类在C#中,这迫使我们测试,如果对象提取和使用它之前存在?

Nullables创建,使原始类型可能为空。 它们的默认值并不非得是实际值(如int,不nullables它的默认值是0,所以是一个0意味着什么0或不设置任何0?)

没有没有什么可以做,以迫使程序员检查的对象为null。 这是好的,但。 这样做会造成开销的数额巨大。 如果这是一个语言功能,多久你会强制检查? 你需要它的时候第一次赋值的变量? 如果该变量指向另一个对象以后呢? 你强迫它的每个方法和属性前检查,如果失败,你会抛出一个异常? 你现在明白了一个空引用异常。 你可能会迫使某人做这超出了你已经得到很少的好处。



Answer 8:

而不是写你自己的类中,你可以使用Microsoft.FSharp.Core.FSharpOption<T>FSharpCore.dll装配。 不幸的是,在C#中使用时,F#类型是一个有点笨拙。

//Create
var none = FSharpOption<string>.None;
var some1 = FSharpOption<string>.Some("some1");
var some2 = new FSharpOption<string>("some2");

//Does it have value?
var isNone1 = FSharpOption<string>.get_IsNone(none);
var isNone2 = OptionModule.IsNone(none);
var isNone3 = FSharpOption<string>.GetTag(none) == FSharpOption<string>.Tags.None;

var isSome1 = FSharpOption<string>.get_IsSome(some1);
var isSome2 = OptionModule.IsSome(some1);
var isSome3 = FSharpOption<string>.GetTag(some2) == FSharpOption<string>.Tags.Some;

//Access value
var value1 = some1.Value; //NullReferenceException when None
var value2 = OptionModule.GetValue(some1); //ArgumentException when None


Answer 9:

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

这是一个事实:

public sealed class Optional<T>
{
    private static readonly Optional<T> EMPTY = new Optional<T>();
    private readonly T value;

    private Optional() => value = default;
    private Optional(T arg) => value = arg.RequireNonNull("Value should be presented");

    public static Optional<T> Empty() => EMPTY;
    public static Optional<T> Of(T arg) => new Optional<T>(arg);
    public static Optional<T> OfNullable(T arg) => arg != null ? Of(arg) : Empty();
    public static Optional<T> OfNullable(Func<T> outputArg) => outputArg != null ? Of(outputArg()) : Empty();

    public bool HasValue => value != null;

    public void ForValuePresented(Action<T> action) => action.RequireNonNull()(value);

    public IOption<T> Where(Predicate<T> predicate) => HasValue 
        ? predicate.RequireNonNull()(value) ? this : Empty() : this;

    public IOption<TOut> Select<TOut>(Func<T, TOut> select) => HasValue 
        ? Optional<TOut>.OfNullable(select.RequireNonNull()(value)) 
        : Optional<TOut>.Empty();

    public IOption<IOption<TOut>> SelectMany<TOut>(Func<T, IOption<TOut>> select) => HasValue 
        ? Optional<IOption<TOut>>.OfNullable(select.RequireNonNull()(value)) 
        : Optional<IOption<TOut>>.Empty();

    public T Get() => value;
    public T GetCustomized(Func<T, T> getCustomized) => getCustomized.RequireNonNull()(value);
    public U GetCustomized<U>(Func<T, U> getCustomized) => getCustomized.RequireNonNull()(value);

    public T OrElse(T other) => HasValue ? value : other;
    public T OrElseGet(Func<T> getOther) => HasValue ? value : getOther();
    public T OrElseThrow<E>(Func<E> exceptionSupplier) where E : Exception => HasValue ? value : throw exceptionSupplier();

    public static explicit operator T(Optional<T> optional) => OfNullable((T) optional).Get();
    public static implicit operator Optional<T>(T optional) => OfNullable(optional);

    public override bool Equals(object obj)
    {
        if (obj is Optional<T>) return true;
        if (!(obj is Optional<T>)) return false;
        return Equals(value, (obj as Optional<T>).value);
    }

    public override int GetHashCode() => base.GetHashCode();
    public override string ToString() => HasValue ? $"Optional has <{value}>" : $"Optional has no any value: <{value}>";

}



Answer 10:

学到了很多卓然霍瓦特的答案。 这里是我的代码。 可选配CAN具有真正的价值或空。 在消费方面,同样的代码处理他们。

void Main()
{
    var myCar = new Car{ Color =  Color.Black, Make="Toyota"};

    Option<Car> optional = Option<Car>.Create(myCar);

    // optional is an Empty 50% of the time.
    if(new Random().NextDouble() > 0.5)
        optional = Option<Car>.CreateEmpty();



    string color = optional
    .Select(car => car.Color.Name)
    .DefaultIfEmpty("<no car>")
    .Single();
    Console.Write(color);
}

class Car {
    public Color Color { get; set; }
    public string Make { get; set;}
}

public class Option<T> : IEnumerable<T>
{
    private readonly T[] data;

    private Option(T[] data)
    {
        this.data = data;
    }

    public static Option<T> Create(T value)
    {
        return new Option<T>(new T[] { value });
    }

    public static Option<T> CreateEmpty()
    {
        return new Option<T>(new T[0]);
    }

    public IEnumerator<T> GetEnumerator()
    {
        return ((IEnumerable<T>)this.data).GetEnumerator();
    }

    System.Collections.IEnumerator
        System.Collections.IEnumerable.GetEnumerator()
    {
        return this.data.GetEnumerator();
    }
}


文章来源: Optional return in C#.Net