将基元类型转换为通用类型?

问题描述 投票:-1回答:1

我正在解析二进制文件。因此,我写了类似下面的东西。

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

然而,这并没有被编译。

不能隐式地将'...'类型转换为'T'

这样做对我来说会非常方便。

如何从一个通用方法中返回任何基元?

c# .net-core type-conversion
1个回答
1
投票

这在检查运行时类型的通用代码中相当常见。这是因为编译器并不 "知道 "字节可以直接转换为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的压力,所以对于性能敏感的代码来说可能是不好的。


1
投票

我不知道你为什么需要这个功能(考虑到你失去了编译时的安全性,是否值得这么麻烦),但你可以尝试通过以下方法去除解决方案中不必要的分配。@Mike Zboray:

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 方法。如果这不适合,那么就对两种实现的基准性能进行测试。

© www.soinside.com 2019 - 2024. All rights reserved.