Как использовать дженерики, чтобы передать аргумент в неуниверсальный способ?
почему следующий код не компилируется? Как я могу создать общий метод, который вызывает соответствующий " BitConverter.Перегрузка GetBytes "основана на том, является ли общий тип "int", "bool", "char" и т. д.? В более общем плане, как я могу создать универсальный метод, который вызывает не-универсальный метод на основе типа универсального параметра?
using System;
public class Test
{
public static void Main()
{
var f = new Foo();
f.GetBytes(10); // should call BitConverter.GetBytes(int);
f.GetBytes(true); // should call BitConverter.GetBytes(bool);
f.GetBytes('A'); // should call BitConverter.GetBytes(char);
}
}
public class Foo
{
public byte[] GetBytes <TSource> (TSource input)
{
BitConverter.GetBytes(input);
}
}
7 ответов
в более общем плане, как я могу создать универсальный метод, который вызывает не универсальный метод на основе типа универсального параметра?
В общем, вы не можете, если только рассматриваемый метод не принимает System.Object
в качестве параметра. Проблема в том, что generic не ограничен только типами, которые были бы разрешены аргументами вызова метода.
самое близкое, что вы можете сделать, это использовать привязку времени выполнения:
public byte[] GetBytes <TSource> (TSource input)
{
dynamic obj = input;
BitConverter.GetBytes(obj);
}
это подталкивает привязку метода логика для выполнения и будет бросать, если нет соответствующего метода для вызова.
причина этого не работает в том, что общие методы по-прежнему разрешают вызовы методов, сделанных в них статически. С TSource
может быть любой тип вообще, он может вызывать только метод на BitConverter
С
где код вызова BitConverter.GetBytes
, типа TSource
, поэтому вызов не может быть статически связан компилятором. Вы можете обойти это с помощью динамического вызова, то есть он будет компилироваться нормально, а затем разрешаться во время выполнения:
…
public byte[] GetBytes(dynamic input)
{
return BitConverter.GetBytes(input);
}
вы заплатите штраф за использование динамического вызова, и если нет подходящего метода для вызова, вы получите исключение во время выполнения.
учитывая, что есть "только" 10 перегрузок BitConverter.GetBytes
, Не невозможно отразить их все явно следующим образом:
public class Foo
{
public byte[] GetBytes(bool input) { return BitConverter.GetBytes(input); }
public byte[] GetBytes(char input) { return BitConverter.GetBytes(input); }
public byte[] GetBytes(double input) { return BitConverter.GetBytes(input); }
public byte[] GetBytes(float input) { return BitConverter.GetBytes(input); }
public byte[] GetBytes(int input) { return BitConverter.GetBytes(input); }
public byte[] GetBytes(short input) { return BitConverter.GetBytes(input); }
public byte[] GetBytes(long input) { return BitConverter.GetBytes(input); }
public byte[] GetBytes(uint input) { return BitConverter.GetBytes(input); }
public byte[] GetBytes(ulong input) { return BitConverter.GetBytes(input); }
public byte[] GetBytes(ushort input) { return BitConverter.GetBytes(input); }
}
это не общий (который вы просили), и не масштабируется до более сложных примеров, но если числа малы, то это an подход к рассмотрению.
Если вы готовы принять удар производительности, вы можете использовать отражение и расширение для объекта GetBytes. образец....
public static class Extensions
{
#region Fields
public static Type bcType;
#endregion
#region Constructor
static Extensions()
{
bcType = typeof(BitConverter);
}
#endregion
public static byte[] GetBytes(this object value)
{
Type typeObj = value.GetType();
MethodInfo miGetBytes = bcType.GetMethod("GetBytes", new Type[] { typeObj });
if (miGetBytes == null)
throw new InvalidOperationException("Method: GetBytes on BitConverter does not have an overload accepting one paramter of type: " + typeObj.FullName);
byte[] bytesRet = (byte[])miGetBytes.Invoke(null, new object[] { obj });
return bytesRet;
}
}
поэтому GetBytes принимает объект. Затем он получает его тип и пытается получить MethodInfo из BitConverter на основе типа передаваемого объекта. Если он не может найти перегрузку, которая принимает этот тип в качестве параметра, он создает исключение InvalidOperation. Если это так, он вызывает его передачу в экземпляре obj в качестве значения и возвращает массив байтов.
например. Код использования,
//make sure the extensions namespace is defined where this code is run.
Console.WriteLine(((ushort)255).GetBytes().ToBase64());
Console.WriteLine(10.0.GetBytes().ToBase64());
Console.WriteLine(((int)2000000000).GetBytes().ToBase64());
Console.WriteLine(((short)128).GetBytes().ToBase64());
//Below causes an error
Console.WriteLine("cool".GetBytes().ToBase64()); //because BitConvert.GetBytes has no overload accepting an argument of type string.
вы должны использовать отражение, чтобы сделать это.
- скачать
GetBytes
группа методов изBitConverter
статический тип. - вытащите перегрузку, для которой первый параметр имеет тип
TSource
. - вызовите этот конкретный метод через
Invoke
метод.
если вы не знакомы с некоторыми из этого, я могу расширить ответ с кодом для этих шагов.
Edit: или просто используйте dynamic
как и другие, предлагают и сохранить немного поработать.
ваш код не компилируется, потому что компилятор не может проверить, что любой тип для TSource
принимается BitConverter.GetBytes()
. Вы можете проверить для каждого типа индивидуально и бросить:
public byte[] GetBytes <TSource> (TSource input)
{
var t = typeof(TSource);
return (t == typeof(int)) ? BitConverter.GetBytes((int) (object) input)
: (t == typeof(bool)) ? BitConverter.GetBytes((bool)(object) input)
: (t == typeof(char)) ? BitConverter.GetBytes((char)(object) input)
: null;
}