#c# #.net #generics
Вопрос:
Учитывая универсальный класс Foo<T>
с методом public T Convert(string value)
, я пытаюсь обработать возможность, которая T
определена как массив.
Например, T
как простой тип:
Foo<int> foo = new Foo<int>();
int bar = foo.Convert("123");
// bar == 123
Но также T
и в виде массива:
Foo<int[]> foo = new Foo<int[]>();
int[] bars = foo.Convert("1,2,3");
// bars = [1, 2, 3]
Вот класс Foo с недопустимым приведением, которое я не совсем уверен, как решить.
public class Foo<T>
{
public T Convert(string value)
{
Type t = typeof(T);
if (t.IsArray)
{
string[] values = value.Split(',');
Type elementType = t.GetElementType();
// this cast is invalid
return (T)values.Select(v => elementType.IsEnum ? Enum.Parse(elementType, v, true) : ChangeType(v, elementType)).ToArray();
}
else
{
return (T)ChangeType(value, t);
}
}
private object ChangeType(string value, Type type)
{
if (type.IsGenericType amp;amp; type.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
{
if (value == null)
{
return defau<
}
type = Nullable.GetUnderlyingType(type);
}
return System.Convert.ChangeType(value, type);
}
}
Комментарии:
1. В строке недопустимого приведения… Вам нужно использовать его как
T
илиT[]
?
Ответ №1:
На самом деле вы здесь не используете функции дженериков, за исключением получения типа с помощью typeof(T). Таким образом, решение без дженериков было бы:
public class Foo
{
public object Convert(string value, Type t)
{
if (t.IsArray)
{
string[] values = value.Split(',');
Type elementType = t.GetElementType();
var array = Array.CreateInstance(elementType, values.Length);
for (var i = 0; i < values.Length; i )
{
array.SetValue(elementType.IsEnum ? Enum.Parse(elementType, values[i], true) : ChangeType(values[i], elementType), i);
}
return array;
}
else
{
return ChangeType(value, t);
}
}
private object ChangeType(string value, Type type)
{
if (type.IsGenericType amp;amp; type.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
{
if (value == null)
{
return defau<
}
type = Nullable.GetUnderlyingType(type);
}
return System.Convert.ChangeType(value, type);
}
}
Обратите внимание, что я использую Array.CreateInstance
, например, для создания правильного int[]
вместо object[]
созданного вами values.Select(v => elementType.IsEnum ? Enum.Parse(elementType, v, true) : ChangeType(v, elementType)).ToArray()
.
Если вы хотите, вы все равно можете иметь универсальную версию поверх нее:
public class Foo<T> : Foo
{
public T Convert(string value)
{
return (T)Convert(value, typeof(T));
}
}
Комментарии:
1. Именно то, что я искал, спасибо! Я обходил это стороной весь день. Я попытался сделать это с Массивом. Создайте объект, и почему-то я просто упустил, что его можно было бы отлить как объект, а затем Т. Очень высоко оценил.
Ответ №2:
Почему бы не сохранить свой impl, который понимает Foo<int>
, и не использовать его для создания массива:
int[] bars = "1,2,3".Split(",").Select(foo.Convert).ToArray();
Таким образом, вызывающий метод может быть тем, кто знает, как устроен массив, а не преобразовывать его в формат CSV:
int[] bars = "1-2-3".Split("-").Select(foo.Convert).ToArray();
Мне действительно интересно, что вы на самом деле выигрываете от обмена фу.Однако конвертируйте для внутреннего анализа.. Похоже, вы пишете большой, запутанный, медленный мега-анализатор для небольшой выгоды, предлагая метод статического анализа для вашего пользовательского типа и вызывая его в соответствии с вышеизложенным