接口类型的通用类型隐式转换失败
Implicit conversion with a generic type failing for an interface type
private struct Maybe<T>
{
private readonly T value;
private readonly bool hasValue;
private Maybe(T value)
{
this.value = value;
hasValue = true;
}
public static implicit operator Maybe<T>(T value) =>
value == null ? new Maybe<T>() : new Maybe<T>(value);
}
private static Maybe<byte> OK()
{
return 5;
}
private static Maybe<IEnumerable<byte>> NotOK()
{
var e = new[] { 1, 2, 3 }.Select(x => (byte)x);
Console.WriteLine(e.GetType().Name);
return e;
}
Fiddle(不要使用):https://dotnetfiddle.net/NxAw9l
已更新 fiddle:https://dotnetfiddle.net/NrARTl
在上面的代码中,某些泛型无法进行隐式转换。请参阅 Ok()
和 NotOk()
函数调用以及 return 类型。一个复杂的通用类型失败了,我不明白为什么。我从 return 类型 IEnumerable<IEnumerable<T>>
的函数中简化了它。这 IEnumerable<T>
仍然失败。我想如果我能理解为什么会失败,我想我也会解决真正的问题。感谢您的帮助和时间。
如果您愿意,这里是错误消息:
Error CS0029 Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<byte>' to 'Maybe<System.Collections.Generic.IEnumerable<byte>>'
更新: 从 NotOK() 返回 Byte[] 无法工作,因为在我的真实源代码中我有一个 LINQ 查询,我必须依赖于它的延迟延迟执行(即必须严格 returning IEnumerable)(参见类似答案 => )。
您不能定义转换 to/from 接口类型,如果您将示例更改为使用 List<T>
而不是 IEnumerable<T>
它将编译 - https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/conversions#user-defined-conversions
改变这个:
private static Maybe<IEnumerable<byte>> NotOK()
{
IEnumerable<byte> a = new byte[] { 1, 2 };
return a;
}
进入这个:
private static Maybe<IEnumerable<byte>> NotOK()
{
var a = new byte[] { 1, 2 };
return a;
}
结构:
private struct Maybe<T>
{
private readonly T value;
private readonly bool hasValue;
private Maybe(T value)
{
this.value = value;
hasValue = true;
}
public static implicit operator Maybe<T>(T value)
{
return value == null ? new Maybe<T>() : new Maybe<T>(value);
}
public bool HasValue(){
return this.hasValue;
}
public T GetValue(){
return this.value;
}
}
private static Maybe<byte> OK()
{
return 5;
}
private static Maybe<IEnumerable<byte>> NotOK()
{
Byte[] a = new byte[] { 1, 2 };
Console.WriteLine(a.GetType().Name);
return a;
}
用法:
public static void Main(string[] args){
var t1 = OK();
var t2 = NotOK();
Console.WriteLine("t1 type is " + t1.GetType().Name);
Console.WriteLine("t2 type is " + t2.GetType().Name);
if(t2.HasValue())
{
List<byte> search = t2.GetValue().Where(b => b > 0).ToList();
foreach(byte num in search){
Console.WriteLine(num);
}
}
}
引用IEnumerable<byte> a
不改变类型,可以继续var
或byte[]
和LINQ
查询,之后,看全文例子
查看完整示例:
https://dotnetfiddle.net/V8RHQe
C# 标准目前不允许隐式转换到接口。
这是在 C# 中实现 Maybe<T>
(或通常称为 Optional<T>
)类型时出现的 well-known 问题。在 C# 语言 github 论坛上正在进行关于此的讨论:
作为解决方法,您可以创建 Maybe<T>
构造函数 internal
并添加静态 non-generic 帮助程序 class:
private static class Maybe
{
public static Maybe<T> From<T>(T value) =>
value == null ? new Maybe<T>() : new Maybe<T>(value);
}
这允许你使用类型推断并编写 Maybe.From(a)
,它比 new Maybe<IEnumerable<byte>>(a)
.
稍微短一点
我将扩展@Heinzi 的回答:
您还可以使用扩展方法:
static class MaybeExtensions
{
public static Maybe<T> AsMaybe<T>(this T value)
{
return new Maybe<T>(value);
}
public static Maybe<TResult> AsMaybe<T, TResult>(this T value)
where T : unmanaged
where TResult : unmanaged
{
return new Maybe<TResult>(Unsafe.As<T, TResult>(ref value));
}
}
并且在您的调用方法中,您可以像这样使用它们:
private static Maybe<IEnumerable<byte>> NotOK()
{
var e = new[] { 1, 2, 3 }.Select(x => (byte)x);
return e.AsMaybe();
}
private static Maybe<byte> OK()
{
return 5.AsMaybe<int, byte>();
}
// Alternatively
private static Maybe<byte> OK()
{
return ((byte)5).AsMaybe();
}
对于可以相互转换的值类型,您需要 AsMaybe<T, TResult>
重载。例如,当您执行 5.AsMaybe()
it returns Maybe<int>
时,如果您的方法的 return 类型是 Maybe<byte>
,您将需要将 Maybe<int>
转换为 Maybe<byte>
,重载会为您完成。
现在,Maybe<T>
中的类型转换运算符变得多余了。您可以使用 var
而不是完整的类型名称:
Maybe<int> obj1 = 5; // use operator
var obj2 = 5.AsMaybe(); // use extension method
IEnumerable 是一个接口。编译器不知道要使用哪种类型。将 ToList()
放在 select 的末尾,如下所示:
private static Maybe<IEnumerable<byte>> NotOK()
{
var e = new[] { 1, 2, 3 }.Select(x => (byte)x).ToList();
Console.WriteLine(e.GetType().Name);
return e;
}
要了解正在发生的事情,请尝试在您的 class 中创建一个方法,然后看着编译器哭泣:)
public static implicit operator Maybe<IEnumerable<T>>(IEnumerable<T> value)
{
return value == null ? new Maybe<IEnumerable<T>>() : new Maybe<IEnumerable<T>>(value);
}
private struct Maybe<T>
{
private readonly T value;
private readonly bool hasValue;
private Maybe(T value)
{
this.value = value;
hasValue = true;
}
public static implicit operator Maybe<T>(T value) =>
value == null ? new Maybe<T>() : new Maybe<T>(value);
}
private static Maybe<byte> OK()
{
return 5;
}
private static Maybe<IEnumerable<byte>> NotOK()
{
var e = new[] { 1, 2, 3 }.Select(x => (byte)x);
Console.WriteLine(e.GetType().Name);
return e;
}
Fiddle(不要使用):https://dotnetfiddle.net/NxAw9l
已更新 fiddle:https://dotnetfiddle.net/NrARTl
在上面的代码中,某些泛型无法进行隐式转换。请参阅 Ok()
和 NotOk()
函数调用以及 return 类型。一个复杂的通用类型失败了,我不明白为什么。我从 return 类型 IEnumerable<IEnumerable<T>>
的函数中简化了它。这 IEnumerable<T>
仍然失败。我想如果我能理解为什么会失败,我想我也会解决真正的问题。感谢您的帮助和时间。
如果您愿意,这里是错误消息:
Error CS0029 Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<byte>' to 'Maybe<System.Collections.Generic.IEnumerable<byte>>'
更新: 从 NotOK() 返回 Byte[] 无法工作,因为在我的真实源代码中我有一个 LINQ 查询,我必须依赖于它的延迟延迟执行(即必须严格 returning IEnumerable)(参见类似答案 =>
您不能定义转换 to/from 接口类型,如果您将示例更改为使用 List<T>
而不是 IEnumerable<T>
它将编译 - https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/conversions#user-defined-conversions
改变这个:
private static Maybe<IEnumerable<byte>> NotOK()
{
IEnumerable<byte> a = new byte[] { 1, 2 };
return a;
}
进入这个:
private static Maybe<IEnumerable<byte>> NotOK()
{
var a = new byte[] { 1, 2 };
return a;
}
结构:
private struct Maybe<T>
{
private readonly T value;
private readonly bool hasValue;
private Maybe(T value)
{
this.value = value;
hasValue = true;
}
public static implicit operator Maybe<T>(T value)
{
return value == null ? new Maybe<T>() : new Maybe<T>(value);
}
public bool HasValue(){
return this.hasValue;
}
public T GetValue(){
return this.value;
}
}
private static Maybe<byte> OK()
{
return 5;
}
private static Maybe<IEnumerable<byte>> NotOK()
{
Byte[] a = new byte[] { 1, 2 };
Console.WriteLine(a.GetType().Name);
return a;
}
用法:
public static void Main(string[] args){
var t1 = OK();
var t2 = NotOK();
Console.WriteLine("t1 type is " + t1.GetType().Name);
Console.WriteLine("t2 type is " + t2.GetType().Name);
if(t2.HasValue())
{
List<byte> search = t2.GetValue().Where(b => b > 0).ToList();
foreach(byte num in search){
Console.WriteLine(num);
}
}
}
引用IEnumerable<byte> a
不改变类型,可以继续var
或byte[]
和LINQ
查询,之后,看全文例子
查看完整示例: https://dotnetfiddle.net/V8RHQe
C# 标准目前不允许隐式转换到接口。
这是在 C# 中实现 Maybe<T>
(或通常称为 Optional<T>
)类型时出现的 well-known 问题。在 C# 语言 github 论坛上正在进行关于此的讨论:
作为解决方法,您可以创建 Maybe<T>
构造函数 internal
并添加静态 non-generic 帮助程序 class:
private static class Maybe
{
public static Maybe<T> From<T>(T value) =>
value == null ? new Maybe<T>() : new Maybe<T>(value);
}
这允许你使用类型推断并编写 Maybe.From(a)
,它比 new Maybe<IEnumerable<byte>>(a)
.
我将扩展@Heinzi 的回答:
您还可以使用扩展方法:
static class MaybeExtensions
{
public static Maybe<T> AsMaybe<T>(this T value)
{
return new Maybe<T>(value);
}
public static Maybe<TResult> AsMaybe<T, TResult>(this T value)
where T : unmanaged
where TResult : unmanaged
{
return new Maybe<TResult>(Unsafe.As<T, TResult>(ref value));
}
}
并且在您的调用方法中,您可以像这样使用它们:
private static Maybe<IEnumerable<byte>> NotOK()
{
var e = new[] { 1, 2, 3 }.Select(x => (byte)x);
return e.AsMaybe();
}
private static Maybe<byte> OK()
{
return 5.AsMaybe<int, byte>();
}
// Alternatively
private static Maybe<byte> OK()
{
return ((byte)5).AsMaybe();
}
对于可以相互转换的值类型,您需要 AsMaybe<T, TResult>
重载。例如,当您执行 5.AsMaybe()
it returns Maybe<int>
时,如果您的方法的 return 类型是 Maybe<byte>
,您将需要将 Maybe<int>
转换为 Maybe<byte>
,重载会为您完成。
现在,Maybe<T>
中的类型转换运算符变得多余了。您可以使用 var
而不是完整的类型名称:
Maybe<int> obj1 = 5; // use operator
var obj2 = 5.AsMaybe(); // use extension method
IEnumerable 是一个接口。编译器不知道要使用哪种类型。将 ToList()
放在 select 的末尾,如下所示:
private static Maybe<IEnumerable<byte>> NotOK()
{
var e = new[] { 1, 2, 3 }.Select(x => (byte)x).ToList();
Console.WriteLine(e.GetType().Name);
return e;
}
要了解正在发生的事情,请尝试在您的 class 中创建一个方法,然后看着编译器哭泣:)
public static implicit operator Maybe<IEnumerable<T>>(IEnumerable<T> value)
{
return value == null ? new Maybe<IEnumerable<T>>() : new Maybe<IEnumerable<T>>(value);
}