Преобразовать примитив в тип generi c? - PullRequest
0 голосов
/ 27 мая 2020

Я разбираю бинарные файлы. Таким образом, я написал что-то вроде следующего:

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

Однако это не компилируется:

Невозможно неявно преобразовать тип '...' в 'T'

Мне было бы очень удобно это сделать.

Как мне вернуть любой примитив из метода generi c?

Ответы [ 2 ]

1 голос
/ 27 мая 2020

Я не знаю, зачем вам эта функция (и стоит ли это того, учитывая, что вы теряете безопасность времени компиляции), но вы можете попробовать это, чтобы удалить ненужные выделения в решении с помощью @ 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. Если это не подходит, то протестируйте производительность обеих реализаций.

1 голос
/ 27 мая 2020

Это довольно часто встречается с общим c кодом, который проверяет типы среды выполнения. Вы должны выполнить преобразование до объекта, а затем преобразовать его в общий c 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
}

Обратной стороной этого является то, что приведение к объекту вызывает операцию упаковки и добавляет к G C давление, поэтому это может быть плохо для кода, чувствительного к производительности.

...