如何为 null 类型实现泛型接口?
How to implement generic interface for null type?
我有以下代码:
public interface IResult
{
StatusCode Code { get; }
string Error { get; }
}
public interface IResult<T> : IResult
{
T Value { get; }
}
public class Result : IResult
{
public StatusCode Code { get; set; }
public string Error { get; set; }
}
public class Result<T> : IResult<T>
{
public StatusCode Code { get; set; }
public string Error { get; set; }
public T Value { get; set; }
}
我有两个接口:IResult
用于没有值的结果(如 void
),IResult<T>
用于需要值的结果。但我不想支持两种不同的类型。我想将它们合并为单一类型。如果结果有值,该值就会存在,而对于我没有任何值的情况,它将只是 null
.
所以它是这样的:
(涵盖所有情况)
public interface IResult<T>
{
StatusCode Code { get; }
string Error { get; }
T Value { get; }
}
public class Result<T> : IResult<T>
{
public StatusCode Code { get; set; }
public string Error { get; set; }
public T Value { get; set; }
}
但我正在努力处理 T
是 empty/null
的情况。我如何为这种情况定义 class ?
到目前为止我只找到一个想法:
public class Result<NullValue> : IResult<NullValue>
{
public StatusCode Code { get; set; }
public string Error { get; set; }
public NullValue Value { get; set; }
}
public class NullValue
{
}
我正在寻找除此之外的任何其他想法:
public class ResultWithNoValue : IResult<T>
{
public StatusCode Code { get; set; }
public string Error { get; set; }
public object Value => null;
}
它不起作用,因为我必须使用:
public interface IBaseResponse< out T>
{
IResult<T> Data { get; }
}
并且此代码不可编译:
public class ResponseWithNoValue : IBaseResponse<object>
{
public ResultWithNoValue Data { get; }
}
And this code is not compilable:
public class ResponseWithNoValue : IBaseResponse<object>
{
public ResultWithNoValue Data { get; }
}
没有,也没有为 Data
使用任何其他实现 class;只能是 IResult<object>
:
public class ResponseWithNoValue : IBaseResponse<object>
{
public IResult<object> Data { get; }
}
该方法可能适合您的需要。
当您创建 Result<object>
的实例时,请不要设置 Value
。接口无论如何都是只获取的,所以它将是不可变的 null:
public class ResponseWithNoValue : IBaseResponse<object>
{
public IResult<object> Data { get; }
public ResponseWithNoValue(StatusCode code, string Error)
{
Data = new Result<object> { Code = code, Error = error };
}
}
var response = new ResponseWithNoValue(SomeCode, "SomeError");
var val = response.Data.Value; // null
response.Data.Value = new object(); // Compilation error
空值的问题在于值类型不能为空。
您可以通过使用 default
而不是 null
来解决这个问题(如果结果是失败的结果,则不初始化 Value
属性)。
话虽这么说,我建议让你的 class 不可变:
public class Result<T> : IResult<T>
{
public Result(T value)
{
Value = value;
}
public Result(StatusCode code, string error)
{
Code = code;
Error = error;
}
public StatusCode Code { get; }
public string Error { get; }
public T Value { get; }
}
更好 - 使用私有构造函数和静态 Success/Fail
方法来创建 Result<T>
class:
的实例
public interface IResult<T>
{
StatusCode Code { get; }
string Error { get; }
T Value { get; }
}
public class Result<T> : IResult<T>
{
public static Result<T> Success(T value)
{
return new Result<T>(null, "", value);
}
public static Result<T> Fail(StatusCode code, string Error)
{
return new Result<T>(code, error, default);
}
private Result(StatusCode code, string error, T value)
{
Code = code;
Error = error;
Value = value;
}
public StatusCode Code { get; }
public string Error { get; }
public T Value { get; }
}
用法:
var result = Result<int>.Success(5);
var result = Result<SomeType>.Fail(new StatusCode(1), "Failed to do something");
这使得通过阅读调用代码很容易理解结果是成功还是失败。
你说你不想有两种类型,但你没有解释为什么。这使得很难理解您实际要解决的问题。
由于问题目前的措辞,没有正当理由想要将这些类型合并为一个。它们不一样。一个有结果,一个没有。
它们之间有一些重叠,但这并不是删除其中一种类型的理由;这是让他们 share/reuse 一些共同逻辑的论据。
本质上,你的论点等同于说:
I don't like having a base class and a derived class, so I'm just going to make the derived class and not use some of the properties when I don't need them.
技术上可行吗?当然。这是个好主意吗?没有。它与静态类型、OOP 原则和一般的干净编码实践背道而驰。
通过合并这两种结果类型,您将忽略无值结果类型,从而导致您面临的确切问题:无法优雅地实现无值结果。
我怀疑您可能忽略了 Result<T>
可以继承自 Result
以及 实现 IResult<T>
接口:
// other interfaces/classes are unchanged
public class Result<T> : Result, IResult<T>
{
public T Value { get; set; }
}
这允许您在处理 Result<T>
对象时 重用 一些 Result
逻辑,以防您不需要与潜在 return 价值。
请注意,这反映了类似的用例,例如异步代码中的 Task
和 Task<T>
return 类型。类型不同是因为它们的处理方式不同。但是,任何 Task<T>
都可以像任何其他 Task
一样对待,因为 Task<T> : Task
.
If a result has a value the value will be there and for cases where I do not have any value it will be just null.
那只是要求空引用异常。有针对 null
的有效案例,但我强烈建议不要创造更多不必要的原因来编写 null
检查。
通过依赖无值结果类型的静态类型,您可以有效地确保不需要空值检查。
由于给定方法 return 是无值结果还是有值结果这一事实无论如何都硬编码在方法主体中,因此方法的 return 类型实际上反映了这一点并没有任何损失还有。
这是让您的消费者清楚他们可以从您的方法中得到什么的问题。通过合并你的结果类型,你使它变得不清楚,因此不必要地混淆了你的界面。
另请注意,我不太认为需要在此处设置 IResult
/IResult<T>
接口,因为您处理的本质上是值对象,但由于您的问题很简单在上下文中,可能有别有用心的原因让接口在这个问题中根本不明显。
我有以下代码:
public interface IResult
{
StatusCode Code { get; }
string Error { get; }
}
public interface IResult<T> : IResult
{
T Value { get; }
}
public class Result : IResult
{
public StatusCode Code { get; set; }
public string Error { get; set; }
}
public class Result<T> : IResult<T>
{
public StatusCode Code { get; set; }
public string Error { get; set; }
public T Value { get; set; }
}
我有两个接口:IResult
用于没有值的结果(如 void
),IResult<T>
用于需要值的结果。但我不想支持两种不同的类型。我想将它们合并为单一类型。如果结果有值,该值就会存在,而对于我没有任何值的情况,它将只是 null
.
所以它是这样的: (涵盖所有情况)
public interface IResult<T>
{
StatusCode Code { get; }
string Error { get; }
T Value { get; }
}
public class Result<T> : IResult<T>
{
public StatusCode Code { get; set; }
public string Error { get; set; }
public T Value { get; set; }
}
但我正在努力处理 T
是 empty/null
的情况。我如何为这种情况定义 class ?
到目前为止我只找到一个想法:
public class Result<NullValue> : IResult<NullValue>
{
public StatusCode Code { get; set; }
public string Error { get; set; }
public NullValue Value { get; set; }
}
public class NullValue
{
}
我正在寻找除此之外的任何其他想法:
public class ResultWithNoValue : IResult<T>
{
public StatusCode Code { get; set; }
public string Error { get; set; }
public object Value => null;
}
它不起作用,因为我必须使用:
public interface IBaseResponse< out T>
{
IResult<T> Data { get; }
}
并且此代码不可编译:
public class ResponseWithNoValue : IBaseResponse<object>
{
public ResultWithNoValue Data { get; }
}
And this code is not compilable:
public class ResponseWithNoValue : IBaseResponse<object> { public ResultWithNoValue Data { get; } }
没有,也没有为 Data
使用任何其他实现 class;只能是 IResult<object>
:
public class ResponseWithNoValue : IBaseResponse<object>
{
public IResult<object> Data { get; }
}
该方法可能适合您的需要。
当您创建 Result<object>
的实例时,请不要设置 Value
。接口无论如何都是只获取的,所以它将是不可变的 null:
public class ResponseWithNoValue : IBaseResponse<object>
{
public IResult<object> Data { get; }
public ResponseWithNoValue(StatusCode code, string Error)
{
Data = new Result<object> { Code = code, Error = error };
}
}
var response = new ResponseWithNoValue(SomeCode, "SomeError");
var val = response.Data.Value; // null
response.Data.Value = new object(); // Compilation error
空值的问题在于值类型不能为空。
您可以通过使用 default
而不是 null
来解决这个问题(如果结果是失败的结果,则不初始化 Value
属性)。
话虽这么说,我建议让你的 class 不可变:
public class Result<T> : IResult<T>
{
public Result(T value)
{
Value = value;
}
public Result(StatusCode code, string error)
{
Code = code;
Error = error;
}
public StatusCode Code { get; }
public string Error { get; }
public T Value { get; }
}
更好 - 使用私有构造函数和静态 Success/Fail
方法来创建 Result<T>
class:
public interface IResult<T>
{
StatusCode Code { get; }
string Error { get; }
T Value { get; }
}
public class Result<T> : IResult<T>
{
public static Result<T> Success(T value)
{
return new Result<T>(null, "", value);
}
public static Result<T> Fail(StatusCode code, string Error)
{
return new Result<T>(code, error, default);
}
private Result(StatusCode code, string error, T value)
{
Code = code;
Error = error;
Value = value;
}
public StatusCode Code { get; }
public string Error { get; }
public T Value { get; }
}
用法:
var result = Result<int>.Success(5);
var result = Result<SomeType>.Fail(new StatusCode(1), "Failed to do something");
这使得通过阅读调用代码很容易理解结果是成功还是失败。
你说你不想有两种类型,但你没有解释为什么。这使得很难理解您实际要解决的问题。
由于问题目前的措辞,没有正当理由想要将这些类型合并为一个。它们不一样。一个有结果,一个没有。
它们之间有一些重叠,但这并不是删除其中一种类型的理由;这是让他们 share/reuse 一些共同逻辑的论据。
本质上,你的论点等同于说:
I don't like having a base class and a derived class, so I'm just going to make the derived class and not use some of the properties when I don't need them.
技术上可行吗?当然。这是个好主意吗?没有。它与静态类型、OOP 原则和一般的干净编码实践背道而驰。
通过合并这两种结果类型,您将忽略无值结果类型,从而导致您面临的确切问题:无法优雅地实现无值结果。
我怀疑您可能忽略了 Result<T>
可以继承自 Result
以及 实现 IResult<T>
接口:
// other interfaces/classes are unchanged
public class Result<T> : Result, IResult<T>
{
public T Value { get; set; }
}
这允许您在处理 Result<T>
对象时 重用 一些 Result
逻辑,以防您不需要与潜在 return 价值。
请注意,这反映了类似的用例,例如异步代码中的 Task
和 Task<T>
return 类型。类型不同是因为它们的处理方式不同。但是,任何 Task<T>
都可以像任何其他 Task
一样对待,因为 Task<T> : Task
.
If a result has a value the value will be there and for cases where I do not have any value it will be just null.
那只是要求空引用异常。有针对 null
的有效案例,但我强烈建议不要创造更多不必要的原因来编写 null
检查。
通过依赖无值结果类型的静态类型,您可以有效地确保不需要空值检查。
由于给定方法 return 是无值结果还是有值结果这一事实无论如何都硬编码在方法主体中,因此方法的 return 类型实际上反映了这一点并没有任何损失还有。
这是让您的消费者清楚他们可以从您的方法中得到什么的问题。通过合并你的结果类型,你使它变得不清楚,因此不必要地混淆了你的界面。
另请注意,我不太认为需要在此处设置 IResult
/IResult<T>
接口,因为您处理的本质上是值对象,但由于您的问题很简单在上下文中,可能有别有用心的原因让接口在这个问题中根本不明显。