Rom*_*ory 4 c# nullable c#-8.0
我正在尝试使用C#8.0中的新Nullable引用类型,并且遇到了这个问题。给定这个结构
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;
}
}
Run Code Online (Sandbox Code Playgroud)
我怎么能告诉编译器,要么Error
或Response
不为空,而且他们不能同时为空?有没有办法用新属性来做这样的事情?
更新结构
结果类型更改为结构时,代码不会更改。要使用结构类型参数,必须将以下约束添加到接口和类型:
where TResult : struct
where TError : struct
Run Code Online (Sandbox Code Playgroud)
当我想到Either模式时,我想到的是F#,模式匹配和区别联合,而不是null。实际上,Either
是避免空值的一种方法。实际上,问题的代码看起来像是尝试创建Result类型,而不仅仅是Either。Scott Wlaschin的“ 铁路定向编程”展示了如何使用这种类型以功能语言实现错误处理。
在F#中,结果类型定义为:
type Result<'T,'TError> =
| Ok of ResultValue:'T
| Error of ErrorValue:'TError
Run Code Online (Sandbox Code Playgroud)
我们现在还不能在C#8中做到这一点,因为还没有歧视的联合。这些计划用于C#9。
模式匹配
我们可以做的是使用模式匹配来获得相同的行为,例如:
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;}
}
Run Code Online (Sandbox Code Playgroud)
这样就无法创建IResult<>
成功与错误并存的方法。可以与模式匹配一起使用,例如:
IResult<int,string> someResult=.....;
if(someResult is Success<int,string> s)
{
//Use s.Result here
}
Run Code Online (Sandbox Code Playgroud)
简化表达
鉴于C#8的属性模式,可以将其重写为:
if(someResult is Success<int,string> {Result: var result} )
{
Console.WriteLine(result);
}
Run Code Online (Sandbox Code Playgroud)
或者,使用开关表达式,一个典型的铁路风格的电话:
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!")
};
}
Run Code Online (Sandbox Code Playgroud)
F#不需要这样做,throw
因为a不可能Result<'T,'TError>
不是Ok
or Error
。在C#中,我们没有这个功能还没有。
switch表达式允许穷举匹配。我认为如果缺少默认子句,编译器也会生成警告。
与解构函数
如果类型具有解构函数,则可以简化表达式,例如:
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;}
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,表达式可以写成:
return data switch {
Error<int,string> e => e,
Success<int,string> (var result) => new Success<int,string>(result*3),
_ => throw new Exception("Unexpected type!")
};
Run Code Online (Sandbox Code Playgroud)
可空性
问题始于可为空的引用类型,那么可为空又如何呢?如果尝试传递nulll,是否会在C#8中收到警告?
是的,只要启用了NRT。此代码:
#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!")
};
}
Run Code Online (Sandbox Code Playgroud)
产生 CS8625: Cannot convert null literal to non-nullable reference type
试
string? s=null;
IResult<string,string> data=new Success<string,string>(s);
Run Code Online (Sandbox Code Playgroud)
产生 CS8604: Possible null reference argument ....
归档时间: |
|
查看次数: |
207 次 |
最近记录: |