首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >可空引用类型和任一模式

可空引用类型和任一模式
EN

Stack Overflow用户
提问于 2019-09-24 00:33:35
回答 3查看 839关注 0票数 5

我正在尝试新的C# 8.0中的可空引用类型,我面临以下问题。

鉴于这一结构:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public readonly struct Either<TReturn, TError>
    where TReturn : struct
    where TError : struct
{
    public TError? Error { get; }
    public TReturn? Response { get; }

    public Either(TError? error, TReturn? response)
    {
        if (error == null && response == null)
        {
           throw new ArgumentException("One argument needs not to be null.");
        }
        if (error != null && response != null)
        {
            throw new ArgumentException("One argument must be null.");
        }
        Error = error;
        Response = response;
    }
}

我如何告诉编译器,ErrorResponse都不是null,它们不能都是null?有办法用新的属性做这样的事情吗?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2019-09-24 00:56:52

结构更新()

当结果类型更改为结构时,代码不会更改。要使用struct类型参数,必须向接口和类型添加以下约束:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
where TResult : struct
where TError  : struct

当我想到任何一种模式时,我想到的是F#、模式匹配和歧视联合,而不是零。实际上,Either是避免空值的一种方法。事实上,这个问题的代码看起来像是试图创建一个结果类型,而不仅仅是一个。Scott的面向铁路的规划展示了如何使用这种类型在函数式语言中实现错误处理。

在F#中,结果类型定义为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
type Result<'T,'TError> = 
    | Ok of ResultValue:'T 
    | Error of ErrorValue:'TError

我们还不能在C# 8中这样做,因为没有歧视的工会。这些都是为C# 9计划的。

模式匹配

我们可以做的是,使用模式匹配来获得相同的行为,例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
interface IResult<TResult,TError>{} //No need for an actual implementation

public class Success<TResult,TError>:IResult<TResult,TError>

{
    public TResult Result {get;}

    public Success(TResult result) { Result=result;}
}

public class Error<TResult,TError>:IResult<TResult,TError>
{
    public TError ErrorValue {get;}

    public Error(TError error) { ErrorValue=error;}
}

这样,就无法创建一个既成功又错误的IResult<>。这可以与模式匹配一起使用,例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
IResult<int,string> someResult=.....;

if(someResult is Success<int,string> s)
{
    //Use s.Result here
}

简化表达式

考虑到C# 8的属性模式,可以将其改写为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if(someResult is Success<int,string> {Result: var result} )
{
    Console.WriteLine(result);
}

或者,使用开关表达式,一个典型的铁路式呼叫:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
IResult<int,string> DoubleIt(IResult<int,string> data)
{
    return data switch {    Error<int,string> e=>e,
                            Success<int,string> {Result: var result}=>
                                       new Success<int,string>(result*2),
                            _ => throw new Exception("Unexpected type!")
                            };
}    

F#不需要throw,因为Result<'T,'TError>不可能是OkError以外的其他东西。在C#中,我们还没有这个特性。

开关表达式允许详尽的匹配。我认为如果缺省子句也丢失了,编译器将生成一个警告。

解析式()

如果类型有解构函数,则表达式可以简化一些,例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Success<TResult,TError>:IResult<TResult,TError>
{
    public TResult Result {get;}

    public Success(TResult result) { Result=result;}

    public void Deconstruct(out TResult result) { result=Result;}
}

public class Error<TResult,TError>:IResult<TResult,TError>
{
    public TError ErrorValue {get;}

    public Error(TError error) { ErrorValue=error;}

    public void Deconstruct(out TError error) { error=ErrorValue;}
}

在这种情况下,表达式可以写成:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
return data switch {    
                Error<int,string> e => e,
                Success<int,string> (var result) => new Success<int,string>(result*3),
                _ => throw new Exception("Unexpected type!")
};

可空性

这个问题从可空引用类型开始,那么可空性呢?如果我们试图传递一个空值,我们会在C# 8中得到警告吗?

是的,只要启用NRT。此代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#nullable enable

void Main()
{
     IResult<string,string> data=new Success<string,string>(null);
     var it=Append1(data);
     Console.WriteLine(it);
}

IResult<string,string> Append1(IResult<string,string> data)
{
    return data switch {    Error<string,string> e=>e,
                            Success<string,string> (var result)=>
                                new Success<string,string>(result+"1"),
                            _ => throw new Exception("Unexpected type!")
                            };
}

通用CS8625: Cannot convert null literal to non-nullable reference type

正在尝试

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
string? s=null;
IResult<string,string> data=new Success<string,string>(s);

生成CS8604: Possible null reference argument ....

票数 7
EN

Stack Overflow用户

发布于 2019-09-24 00:49:31

在实现Either monad时,应该使用两个不同的构造函数。这样,您就可以轻松地避开这些检查,因为您的实现将确保无法同时分配两个属性。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public readonly class Either<TReturn, TError>
{
    bool _successful;
    private TError _error { get; }
    private TReturn _response { get; }

    public Either(TError error)
    {
        _error = error;
    }

    public Either(TReturn response)
    {
       _successful = true;
       _response = response;
    }
}

此外,还需要添加一个方法(到struct中),该方法将用于从结构中提取值,并将其转换为公共返回类型:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public Match<T>(Func<TError, T> errorFunc, Func<TResponse, T> successFunc)
    => _successful ? successFunc(_response) : errorFunc(_error);

这样,您就强制用户处理这两种情况(成功、错误),并提供将转换为公共类型的函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var errorEither = new Either<string, int>(10); // example of error code
var successEither = new Either<string, int>("success"); // example of success

var commonValueError = errorEither.Match<bool>(err => false, succ => true);
var commonValueSuccess = successEither.Match<bool>(err => false, succ => true);
票数 2
EN

Stack Overflow用户

发布于 2019-09-24 10:21:01

你可以用Resharper ContractAnnotation做类似的事情。这不是C# 8的特异性,(但是.我不认为您的示例实际上使用的是可空引用类型,对吗?)您使用的是可空结构。)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[ContractAnnotation(
   "=> pError: null, pResponse: notnull; => pError: notnull, pResponse: null"
)]
public void Get(out TError? pError, out TReturn? pResponse) {
   pError = Error;
   pResponse = Response;
}

(字符串的意思是,=>的左边是输入条件,=>的右边是输出条件,;将不同的情况分开,一个无标签的值引用方法返回值。因此,在这种情况下:不管输入是什么,输出条件都是null/notnull或notnull/null。

然后使用C# 7特性out var

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
GetAnEitherFromSomewhere()
.Get(out var error, out var response);
if (error != null) {
   // handle error
   return;
}

// response is now known to be not null, because we can only get here if error was null

老实说,我发现JetBrains的注释[NotNull][CanBeNull][ContractAnnotation]比可空引用类型灵活得多(尽管更详细)。基本上,它们允许中间情况,在这种情况下,值可以是null,但也存在值不能是null的情况,而且在运行时可以区分这些情况。对于可空引用类型,我不能指定中间的情况,我必须选择绝对可空或绝对不可空。

即使是像TryParse这样普通的东西

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// should the result be nullable with nullable reference types on?
// you have to either lie with ! or else use ? and assume it can always be null
public bool TryParse(string pString, out SomeClass pResult) {
   if (<cant parse>) {
      pResult = null;
      return false;
   }

   pResult = value;
   return true;
}

// works great with JetBrains annotations and nullable reference types off
// now you can know that the result is null or notnull
// based on testing the bool return value
[ContractAnnotation("=> true, pResult: notnull; => false, pResult: null")]
public bool TryParse(string pString, out SomeClass pResult) {
   ...
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58076171

复制
相关文章
Nullable Reference Types 可空引用类型
但如果想避免NullReferenceException的发生,确实需要做很多麻烦的工作。
solenovex
2019/10/15
8190
Nullable Reference Types 可空引用类型
C# 堆与栈、值类型与引用类型、可空类型
栈是一种先进后出的数据结构,是编译期间就分配好的内存空间,因此你的代码中必须就栈的大小有明确的定义。栈中每个指针(当运行到那个变量时)会指向堆中的某一内存区域或说是空间。 堆(heap)就直接是内存区域了,它是为了栈的引用而开发内存的。通常内置变量就是值类型是被保存在栈中的。其他由.NET框架(Framework)提供的,或者是我们自己定义的对象即引用类型,一般被创建在堆中并将由栈中变量引用。是程序运行期间动态分配的内存空间,你可以根据程序的运行情况确定要分配的堆内存的大小。
aehyok
2019/02/25
9830
[深入解析C#] 可空值类型
Tony Hoare于1965年在Algol语言中首次引入了null引用的概念,后来他把这项举措称为“十亿美金的过失”。无数开发人员饱受NullReferenceException(.NET)、NullPointerException(Java)等的折磨。由于此类问题的普遍性,Stack Overflow上有大量与之相关的典型问题。既然可空特性如此声名狼藉,为何C# 2以及.NET 2.0要引入可空值类型呢?
科控物联
2022/03/29
2.3K0
C#基础知识系列二(值类型和引用类型、可空类型、堆和栈、装箱和拆箱)
  之前对几个没什么理解,只是简单的用过可空类型,也是知道怎么用,至于为什么,还真不太清楚,通过整理本文章学到了很多知识,也许对于以后的各种代码优化都有好处。
aehyok
2018/08/31
1.2K0
C#基础知识系列二(值类型和引用类型、可空类型、堆和栈、装箱和拆箱)
C#基础知识系列二(值类型和引用类型、可空类型、堆和栈、装箱和拆箱)
  之前对几个没什么理解,只是简单的用过可空类型,也是知道怎么用,至于为什么,还真不太清楚,通过整理本文章学到了很多知识,也许对于以后的各种代码优化都有好处。
aehyok
2019/02/25
1.1K0
C# 可空类型
C#的可空引用类型是一项功能,允许开发人员在变量、参数、字段、属性和返回值等可能为null的情况下,明确地表示其可以为null,或者不能为null。这有助于避免在程序运行时出现空引用异常(Null Reference Exception),提高了代码的安全性和可靠性。通过使用可空引用类型,开发人员可以更早地捕捉到潜在的空引用问题,从而减少了因为空引用而引起的错误。
JusterZhu
2023/10/06
2980
C# 可空类型
C# 可为空引用类型
我对 C# 钟爱有加,我认为它严谨的语言设计非常棒。尽管如此,就目前而言,即使在 C# 版本 7 发布后,此语言也仍称不上完美。我这里指的是,尽管有理由期望 C# 会一直不断添加新功能,但遗憾的是,同时也存在着一些问题。
郑子铭
2023/08/29
1920
C# 可为空引用类型
C#可空值类型
1. 可空类型修饰符(?)   引用类型可以使用空引用表示一个不存在的值,而值类型通常不能表示为空。   例如:string str=null; 是正确的,int i=null; 编译器就会报错。
_一级菜鸟
2021/08/10
1.4K0
Swift可空(Optional)类型基础
可空类型,对于熟悉C#的同学一定不会陌生。在C#里面值类型都是不能为空的,比如int类型默认为0,bool默认为false。但是我们给int加上?后,就是一个可空类型了。
MJ.Zhou
2022/05/07
8660
Swift可空(Optional)类型基础
Kotlin中的可空类型
Java的NullPointException是经常遇到的异常,也是最让人头疼的一个异常。Kotlin为了解决这个问题,引进了可空类型,将运行时可能发生异常提前到编译期发现。 Kotlin中有可空类型,这种类型表示取值可能为空;而一般类型,则取值不能为空。区别是类型后面有一个?,表示这个类型是可空的。 举个栗子:
用户1108631
2019/08/17
1.5K0
C# 8.0 可空引用类型中的各项警告/错误的含义和示例代码
C# 8.0 引入了可为空引用类型和不可为空引用类型。当你需要给你或者团队更严格的要求时,可能需要定义这部分的警告和错误级别。
walterlv
2023/10/22
8770
C# 8.0 可空引用类型中的各项警告/错误的含义和示例代码
第3章 Kotlin 可空类型与类型系统第3章 Kotlin 可空类型与类型系统
我们在编程语言中使用类型的目的是为了让编译器能够确定类型所关联的对象需要分配多少空间。
一个会写诗的程序员
2018/08/17
2.1K0
第3章 Kotlin 可空类型与类型系统第3章 Kotlin 可空类型与类型系统
C# 8.0 如何在项目中开启可空引用类型的支持
C# 8.0 引入了可为空引用类型和不可为空引用类型。由于这是语法级别的支持,所以比传统的契约式编程具有更强的约束力。更容易帮助我们消灭 null 异常。
walterlv
2023/10/22
3570
[C#2] 4-可空类型、静态类
1. 可空类型 值类型是不可以为null的[即不可为空值], 假如我们想让它为null呢[比如它对映这数据库中的某个表的某个字段,但是这个字段是null]。 自己实现的话,简单的写一个类,有一个值类型的字段,检查该字段是否初始化,是的话返回该值,否的话返回null。 假如是在C#1.0时,那么每个值类型都要写这么一份代码了。C#2.0有了泛型的支持,所以我们可以定义一个泛型版的。 幸运的是C#2.0带来了一个新的类型[System.Nullable<T>]帮我们实现了,T就是需要传入的类型[约束为值类型].
blackheart
2018/01/19
8220
C# 8.0 的可空引用类型,不止是加个问号哦!你还有很多种不同的可空玩法
C# 8.0 引入了可空引用类型,你可以通过 ? 为字段、属性、方法参数、返回值等添加是否可为 null 的特性。
walterlv
2023/10/22
1.6K0
C# 8.0 的可空引用类型,不止是加个问号哦!你还有很多种不同的可空玩法
C# 可空引用类型 Nullable 更强制的约束:将警告改为错误 WarningsAsErrors
于是 C# 8.0 带来的可空引用类型由于默认以警告的形式出现,所以实际上约束力非常弱。
walterlv
2023/10/22
4030
C# 可空引用类型 Nullable 更强制的约束:将警告改为错误 WarningsAsErrors
.NET中可空值类型实现原理
为了让.Net中的值类型可以赋值为null,微软特地添加了Nullable<T>类型,也可简写为T?。但是Nullable<T>自身是结构体,也是值类型,那么它是如何实现将null赋值给值类型的呢?
雪飞鸿
2018/09/05
1K0
.NET中可空值类型实现原理
【Kotlin】空安全 ① ( Kotlin 的空安全机制 | 变量可空性 | 默认变量不可赋空值 | 声明可空类型变量 )
在 Java 语言 编写的程序中 , 出现最多的崩溃就是 NullPointerException 空指针异常 ,
韩曙亮
2023/03/30
1.9K0
【Kotlin】空安全 ① ( Kotlin 的空安全机制 | 变量可空性 | 默认变量不可赋空值 | 声明可空类型变量 )
可空类型及其衍生运算符
我们知道,值类型在使用前必须设置值,而引用类型则可以是null,但在某些情况下,为值类型设置为空是必要的(如处理数据库数据的时候),微软因此推出了可空类型 System.Nullable<T> 这是一个泛型类,其中,T就代表一个具体的值类型。
宿春磊Charles
2022/03/29
4080
可空类型及其衍生运算符
c#中的可空类型和空合并操作符(Nullable Types 和 Null Coalescing Operator)
在本文中,我们将讨论可空类型和空合并操作符以及如何在基于c#的代码中使用它们。 这是c#编程中的一个基本概念。在这里,我将解释可空类型,c#中的空合并操作符,以及如何在LINQ中使用该操作符。 c#中
程序你好
2018/07/20
4.1K0

相似问题

可空引用类型和选项模式

46

“不是”模式和可空类型

311

可空引用类型和MSTest ClassInitialize

13

可空引用类型和实现接口

12

可空引用类型和ToString()重载

12
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文