C#.Net 中的可选返回

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16199227/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 00:21:40  来源:igfitidea点击:

Optional return in C#.Net

c#.netnullableoptional

提问by Hikari

Java 1.8 is receiving the Optional class, that allows us to explicitly say when a method may return a null value and "force" its consumer to verify if it is not null (isPresent()) before using it.

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

I see C# has Nullable, that does something similar, but with basic types. It seems to be used for DB queries, to distinguish when a value exists and is 0 from when it doesn't exist and is null.

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

But it seems that C#'s Nullable doesn't work for objects, only for basic types, while Java's Optional only works for objects and not for basic types.

但是好像C#的Nullable对对象不起作用,只对基本类型起作用,而Java的Optional只对对象起作用,对基本类型不起作用。

Is there a Nullable/Optional class in C#, that forces us to test if object exists before extracting and using it?

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

采纳答案by Servy

Not in the language, no, but you can make your own:

不是在语言中,不,但您可以自己制作:

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;
    }
}

Note that you won't be able to emulate certain behaviors of Nullable<T>, such as the ability to box a nullable value with no value to null, rather than a boxed nullable, as it has special compiler support for that (and a some other) behavior.

请注意,您将无法模拟 的某些行为Nullable<T>,例如能够将没有值的可空值装箱为空,而不是装箱的可空值,因为它具有对该(和其他一些)行为的特殊编译器支持.

回答by kemiller2002

Is there a Nullable/Optional class in C#, that forces us to test if object exists before extracting and using it?

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

Nullables were created so that primitive types could be null. Their default value didn't have to be an actual value (Like int, without nullables it's default is 0, so is that a 0 means something 0 or a not set to anything 0?)

创建 Nullables 以便原始类型可以为 null。它们的默认值不必是实际值(像 int 一样,如果没有可空值,它的默认值为 0,那么 0 是指 0 还是未设置为 0?)

No there is nothing that you can do to force a programmer to check if an object is null. That's good though. Doing so would create an immense amount of overhead. If this was a language feature, how often would you force check? Would you require it when the variable is first assigned? What if the variable points to another object later? Would you force it to check before every method and property, and if it fails would you throw an exception? You get that now with a null reference exception. You would get very little benefit in forcing someone to do this beyond what you already have.

不,您无法强制程序员检查对象是否为空。不过这很好。这样做会产生大量的开销。如果这是一个语言特性,你多久会强制检查一次?首次分配变量时是否需要它?如果变量稍后指向另一个对象怎么办?你会强制它在每个方法和属性之前检查,如果失败你会抛出异常吗?你现在得到了一个空引用异常。强迫某人做超出你已有的事情,你不会得到什么好处。

回答by Lee

There's nothing built-in, but you can define your own. Note that an Option<T>implementation doesn't make sense without defining the map/bind operators.

没有内置的东西,但你可以定义你自己的。请注意,如果Option<T>不定义 map/bind 操作符,实现就没有意义。

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;
    }
}

回答by Luká? Kmoch

There is better implementation of option type in C#. You can find this implemenation in Tactical design patterns in .NETby Zoran Horvat at pluralsight.com. It includes an explanation why and how to use it. The basic idea is to implement option class as implementation of IEnumerable<> interface.

C# 中有更好的选项类型实现。您可以Zoran Horvat 在 pluralsight.com 的Tactical design patterns in .NET 中找到此实现。它包括解释为什么以及如何使用它。基本思想是将选项类实现为 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();
    }
}

回答by aloon

In the project "C# functional language extensions" https://github.com/louthy/language-extexists the Option object of F# among others functional patters

在“C# 函数式语言扩展”项目https://github.com/louthy/language-ext 中存在 F# 的 Option 对象以及其他函数式模式

回答by Zoran Horvat

In my opinion, any Optionimplementation which exposes HasValueproperty is the defeat of the entire idea. The point of optional objects is that you can make unconditional calls to their contents without testing whether the content is there.

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

If you have to test whether the optional object contains a value, then you have done nothing new compared to common nulltests.

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

Here is the article in which I am explaining optional objects in full detail: Custom Implementation of the Option/Maybe Type in C#

这是我详细解释可选对象的文章:C# 中选项/可能类型的自定义实现

And here is the GitHub repository with code and examples: https://github.com/zoran-horvat/option

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

If you're reluctant to use a heavyweight Option solution, then you can easily build a lightweight one. You can make your own Option<T>type which implements IEnumerable<T>interface, so that you can leverage LINQ extension methods to turn calls optional. Here is the simplest possible implementation:

如果您不愿意使用重量级的 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();
    }
}

Using this Option<T>type is done via LINQ:

使用这种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

You can find more about optional objects in these articles:

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

And you may refer to my video courses for more details on how to simplify control flow using Optiontype and other means: Making Your C# Code More Functionaland Tactical Design Patterns in .NET: Control Flow

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

The first video course (Making Your C# Code More Functional) brings detailed introduction to railway-oriented programming, including the Eitherand Optiontypes and how they can be used to manage optional objects and handle exceptional cases and errors.

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

回答by tearvisus

Instead of writing your own class, you could use Microsoft.FSharp.Core.FSharpOption<T>from the FSharpCore.dllassembly. Unfortunately, the F# types are a bit clumsy when used in C#.

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

//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

回答by gileCAD

Perhaps this is closer to the F# Option type

也许这更接近于 F# Option 类型

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;
}

回答by E. Anagnostopoulos

I decided to implement some kind of Optional<> Java class prototype some time ago using one of the last C# version.

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

Here it is in fact:

实际上是这样的:

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}>";

}

}

回答by Rm558

Learned a lot from Zoran Horvat's answer. Here is my code. optional can has a real value or an empty. On the consuming side, same code handle them all.

从 Zoran Horvat 的回答中学到了很多。这是我的代码。可选可以有一个真实的值或一个空的。在消费方面,相同的代码处理所有这些。

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();
    }
}