【问题标题】:Convert primitive to generic type?将原始类型转换为泛型类型?
【发布时间】:2020-05-26 21:01:24
【问题描述】:

我正在解析二进制文件。因此,我写了类似于以下内容的内容:

public T Parse<T>(BinaryReader reader)
{
    if (typeof(T) == typeof(byte))
        return reader.ReadByte();
    else if (typeof(T) == typeof(int))
        return reader.ReadInt32();
    // ...
}

但是这不能编译:

无法将类型“...”隐式转换为“T”

这样做对我来说很方便。

如何从泛型方法返回任何原语?

【问题讨论】:

  • "但是这不能编译。"意味着您遇到了编译错误。这是什么?
  • 你必须像return (byte)(object)reader.ReadByte() 一样投射,但这让我想知道调用者通过调用Parse&lt;byte&gt;(reader) 而不是read.ReadByte() 得到了什么。您将对该方法返回的任何值类型进行装箱。
  • @ZoharPeled 添加了错误。
  • @Cobra_Fast 哦,对不起。我的意思是return (T)(object)reader.ReadByte()

标签: c# .net-core type-conversion


【解决方案1】:

这在检查运行时类型的通用代码中相当常见。您必须向上转换为对象,然后向下转换为通用 T。这是因为编译器不“知道”字节可直接转换为 T,即使由于类型检查它与 T 相同。

public T Parse<T>(BinaryReader reader)
{
    if (typeof(T) == typeof(byte))
        return (T)(object)reader.ReadByte();
    else if (typeof(T) == typeof(int))
        return (T)(object)reader.ReadInt32();
    // all returns will need similar casts
}

这样做的缺点是对对象的强制转换会导致装箱操作并增加 GC 压力,因此可能对性能敏感的代码不利。

【讨论】:

【解决方案2】:

我不知道您为什么需要此功能(如果值得麻烦,考虑到您正在失去编译时安全性),但您可以尝试通过 @987654321 删除解决方案中不必要的分配@:

public static T Parse<T>(BinaryReader reader)
{
    return ReaderProvider<T>.ReaderFunc(reader);
}

class ReaderProvider<T> 
{
    public static readonly Func<BinaryReader, T> ReaderFunc;
    static ReaderProvider()
    {
        MethodInfo mi ;
        if(typeof(T) == typeof(byte))
        {
            mi = typeof(BinaryReader).GetMethod(nameof(BinaryReader.ReadByte));
        }
        else if(typeof(T) == typeof(int))
        {
            mi = typeof(BinaryReader).GetMethod(nameof(BinaryReader.ReadInt32));
        }
        // ....
        else
        {
            throw new ArgumentOutOfRangeException($"{typeof(T).FullName}"); 
        }
        var p = Expression.Parameter(typeof(BinaryReader));
        ReaderFunc = Expression.Lambda<Func<BinaryReader, T>>(Expression.Call(p, mi), p).Compile();
    }
}

示例用法:

var ms = new MemoryStream();
ms.Write(new byte[] { 1, 2, 0, 0, 0 }, 0, 5);
ms.Seek(0, SeekOrigin.Begin);
var x = new BinaryReader(ms);

Console.WriteLine(Parse<byte>(x));
Console.WriteLine(Parse<int>(x));

我也强烈建议坚持使用BinaryReader.ReadX 方法。如果这不适合这两种实现的基准性能。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-28
    • 1970-01-01
    • 1970-01-01
    • 2011-02-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多