在 C# 2.0 中使用泛型将定界字符串转换为数组枚举
Convert delimited string to array enum using generics in C# 2.0
我需要将逗号分隔的字符串转换为泛型 class 中的枚举类型数组。
问题是我需要创建一个基于泛型 T 的数组。
这是我的代码:
class Program
{
static void Main(string[] args)
{
var testIt = new TestIt<TestValues[]>();
TestValues[] converted = testIt.Convert("Pizza,Sub");
}
}
public class TestIt<T>
{
public T Convert(string delimitedValues)
{
var valueType = typeof(T);
var elementType = valueType;
if (!valueType.IsArray)
{
throw new Exception("T is not an array");
}
if (valueType.HasElementType)
{
elementType = valueType.GetElementType();
}
var elements = delimitedValues.Split(',');
foreach (var elementValue in elements)
{
var newElement = Enum.Parse(elementType, elementValue.Trim(), true);
// not sure what I can do with the element here
}
}
}
public enum TestValues
{
Unknown,
Pizza,
Sub,
Burger
}
关于如何执行此操作的任何想法?我被难住了!
我试过创建枚举类型的对象数组,但似乎无法将其转换为 T。
请记住,这是 .Net Framwork 2.0,所以我的工具箱有限。
感谢任何人提供的任何想法。
只需对代码进行最少的更改:您可以使用 Array.CreateInstance
,因为您已经知道数组长度(elements.Length
在 Split()
之后),然后调用 Convert.ChangeType()
来能够将 Array
转换为 T
:
public T Convert(string delimitedValues)
{
var valueType = typeof(T);
var elementType = valueType;
if (!valueType.IsArray)
{
throw new Exception("T is not an array");
}
if (valueType.HasElementType)
{
elementType = valueType.GetElementType();
}
var elements = delimitedValues.Split(',');
var arrayToReturn = Array.CreateInstance(elementType, elements.Length);
for (int i = 0; i < elements.Length; i++ )
{
var newElement = Enum.Parse(elementType, elements[i].Trim(), true);
arrayToReturn.SetValue(newElement, i);
}
return (T)System.Convert.ChangeType(arrayToReturn, valueType);
}
试试这个 class:
public class TestIt
{
public static T[] Convert<T>(string delimitedValues)
{
if (!typeof(T).IsEnum)
{
throw new ArgumentException();
}
if (delimitedValues == string.Empty)
{
return new T[0];
}
string[] parts = delimitedValues.Split(',');
T[] converted = Array.ConvertAll(parts, x => (T)Enum.Parse(typeof(T), x));
return converted;
}
}
按照惯例,您使用 T
作为通用参数而不是 T[]
像这样使用它:
TestValues[] values = TestIt.Convert<TestValues>("Unknown,Pizza");
少一个施法也能做到:
public static T[] Convert<T>(string delimitedValues) where T : struct
{
if (!typeof(T).IsEnum)
{
throw new ArgumentException();
}
if (delimitedValues == string.Empty)
{
return new T[0];
}
string[] parts = delimitedValues.Split(',');
T[] converted = new T[parts.Length];
for (int i = 0; i < parts.Length; i++)
{
if (!Enum.TryParse(parts[i], out converted[i]))
{
throw new FormatException(parts[i]);
}
}
return converted;
}
如果你愿意,你甚至可以拥有一个 return T?[]
的版本(因此是一个可为 null 的枚举数组)
public static T?[] ConvertNullable<T>(string delimitedValues) where T : struct
{
if (!typeof(T).IsEnum)
{
throw new ArgumentException();
}
if (delimitedValues == string.Empty)
{
return new T[0];
}
string[] parts = delimitedValues.Split(',');
T?[] converted = new T?[parts.Length];
for (int i = 0; i < parts.Length; i++)
{
if (parts[i] == string.Empty)
{
continue;
}
T value;
if (!Enum.TryParse(parts[i], out value))
{
throw new FormatException(parts[i]);
}
converted[i] = value;
}
return converted;
}
喜欢就用;
TestValues?[] values = TestIt.ConvertNullable<TestValues>(",Unknown,,Pizza,");
请注意,最后一个版本不会跳过 "invalid" 值,它仍然会抛出这些值。只需将 string.Empty
元素转换为 null
。可悲的是,有一个问题:如果你 ConvertNullable<TestValues>(string.Empty)
它将 return 一个 TestValues[0]
,但该字符串甚至可以转换为 TestValues[1] { null }
.
现在,如果你真的想要一个什么都有的披萨:
public static class EnumSplitter
{
public static T[] Convert<T>(string delimitedValues) where T : struct
{
if (!typeof(T).IsEnum)
{
throw new ArgumentException();
}
if (delimitedValues == string.Empty)
{
return new T[0];
}
string[] parts = delimitedValues.Split(',');
T[] converted = new T[parts.Length];
for (int i = 0; i < parts.Length; i++)
{
if (!Enum.TryParse(parts[i], out converted[i]))
{
throw new FormatException(parts[i]);
}
}
return converted;
}
public static TArray ConvertArray<TArray>(string delimitedValues) where TArray : IList
{
return MethodCache<TArray>.Convert(delimitedValues);
}
public static T?[] ConvertNullable<T>(string delimitedValues) where T : struct
{
if (!typeof(T).IsEnum)
{
throw new ArgumentException();
}
if (delimitedValues == string.Empty)
{
return new T?[0];
}
string[] parts = delimitedValues.Split(',');
T?[] converted = new T?[parts.Length];
for (int i = 0; i < parts.Length; i++)
{
if (parts[i] == string.Empty)
{
continue;
}
T value;
if (!Enum.TryParse(parts[i], out value))
{
throw new FormatException(parts[i]);
}
converted[i] = value;
}
return converted;
}
public static TArray ConvertNullableArray<TArray>(string delimitedValues) where TArray : IList
{
return MethodCache<TArray>.Convert(delimitedValues);
}
private static class MethodCache<TArray> where TArray : IList
{
public static readonly Func<string, TArray> Convert;
static MethodCache()
{
if (!typeof(TArray).IsArray)
{
throw new ArgumentException("TArray");
}
Type element = typeof(TArray).GetElementType();
Type element2 = Nullable.GetUnderlyingType(element);
if (element2 == null)
{
Convert = (Func<string, TArray>)Delegate.CreateDelegate(typeof(Func<string, TArray>), typeof(EnumSplitter).GetMethod("Convert").MakeGenericMethod(element));
}
else
{
Convert = (Func<string, TArray>)Delegate.CreateDelegate(typeof(Func<string, TArray>), typeof(EnumSplitter).GetMethod("ConvertNullable").MakeGenericMethod(element2));
}
}
}
}
我使用 MethodCache<>
class 来缓存对 "right" Convert<>
方法的反射调用。
使用:
TestValues[] arr1 = EnumSplitter.Convert<TestValues>("Unknown,Pizza");
TestValues?[] arr2 = EnumSplitter.ConvertNullable<TestValues>("Unknown,,Pizza,");
TestValues[] arr3 = EnumSplitter.ConvertArray<TestValues[]>("Unknown,Pizza");
TestValues?[] arr4 = EnumSplitter.ConvertNullableArray<TestValues?[]>("Unknown,,Pizza,");
public static T[] ToEnumsArray<T>(this string stringValuesWithDelimeter) where T : struct
{
var result = new List<T>();
if (!string.IsNullOrWhiteSpace(stringValuesWithDelimeter))
{
var arr = stringValuesWithDelimeter.Split(',');
foreach (var item in arr)
{
if (Enum.TryParse(item, true, out T enumResult))
{
result.Add(enumResult);
}
}
}
return result.ToArray();
}
我需要将逗号分隔的字符串转换为泛型 class 中的枚举类型数组。
问题是我需要创建一个基于泛型 T 的数组。
这是我的代码:
class Program
{
static void Main(string[] args)
{
var testIt = new TestIt<TestValues[]>();
TestValues[] converted = testIt.Convert("Pizza,Sub");
}
}
public class TestIt<T>
{
public T Convert(string delimitedValues)
{
var valueType = typeof(T);
var elementType = valueType;
if (!valueType.IsArray)
{
throw new Exception("T is not an array");
}
if (valueType.HasElementType)
{
elementType = valueType.GetElementType();
}
var elements = delimitedValues.Split(',');
foreach (var elementValue in elements)
{
var newElement = Enum.Parse(elementType, elementValue.Trim(), true);
// not sure what I can do with the element here
}
}
}
public enum TestValues
{
Unknown,
Pizza,
Sub,
Burger
}
关于如何执行此操作的任何想法?我被难住了!
我试过创建枚举类型的对象数组,但似乎无法将其转换为 T。
请记住,这是 .Net Framwork 2.0,所以我的工具箱有限。
感谢任何人提供的任何想法。
只需对代码进行最少的更改:您可以使用 Array.CreateInstance
,因为您已经知道数组长度(elements.Length
在 Split()
之后),然后调用 Convert.ChangeType()
来能够将 Array
转换为 T
:
public T Convert(string delimitedValues)
{
var valueType = typeof(T);
var elementType = valueType;
if (!valueType.IsArray)
{
throw new Exception("T is not an array");
}
if (valueType.HasElementType)
{
elementType = valueType.GetElementType();
}
var elements = delimitedValues.Split(',');
var arrayToReturn = Array.CreateInstance(elementType, elements.Length);
for (int i = 0; i < elements.Length; i++ )
{
var newElement = Enum.Parse(elementType, elements[i].Trim(), true);
arrayToReturn.SetValue(newElement, i);
}
return (T)System.Convert.ChangeType(arrayToReturn, valueType);
}
试试这个 class:
public class TestIt
{
public static T[] Convert<T>(string delimitedValues)
{
if (!typeof(T).IsEnum)
{
throw new ArgumentException();
}
if (delimitedValues == string.Empty)
{
return new T[0];
}
string[] parts = delimitedValues.Split(',');
T[] converted = Array.ConvertAll(parts, x => (T)Enum.Parse(typeof(T), x));
return converted;
}
}
按照惯例,您使用 T
作为通用参数而不是 T[]
像这样使用它:
TestValues[] values = TestIt.Convert<TestValues>("Unknown,Pizza");
少一个施法也能做到:
public static T[] Convert<T>(string delimitedValues) where T : struct
{
if (!typeof(T).IsEnum)
{
throw new ArgumentException();
}
if (delimitedValues == string.Empty)
{
return new T[0];
}
string[] parts = delimitedValues.Split(',');
T[] converted = new T[parts.Length];
for (int i = 0; i < parts.Length; i++)
{
if (!Enum.TryParse(parts[i], out converted[i]))
{
throw new FormatException(parts[i]);
}
}
return converted;
}
如果你愿意,你甚至可以拥有一个 return T?[]
的版本(因此是一个可为 null 的枚举数组)
public static T?[] ConvertNullable<T>(string delimitedValues) where T : struct
{
if (!typeof(T).IsEnum)
{
throw new ArgumentException();
}
if (delimitedValues == string.Empty)
{
return new T[0];
}
string[] parts = delimitedValues.Split(',');
T?[] converted = new T?[parts.Length];
for (int i = 0; i < parts.Length; i++)
{
if (parts[i] == string.Empty)
{
continue;
}
T value;
if (!Enum.TryParse(parts[i], out value))
{
throw new FormatException(parts[i]);
}
converted[i] = value;
}
return converted;
}
喜欢就用;
TestValues?[] values = TestIt.ConvertNullable<TestValues>(",Unknown,,Pizza,");
请注意,最后一个版本不会跳过 "invalid" 值,它仍然会抛出这些值。只需将 string.Empty
元素转换为 null
。可悲的是,有一个问题:如果你 ConvertNullable<TestValues>(string.Empty)
它将 return 一个 TestValues[0]
,但该字符串甚至可以转换为 TestValues[1] { null }
.
现在,如果你真的想要一个什么都有的披萨:
public static class EnumSplitter
{
public static T[] Convert<T>(string delimitedValues) where T : struct
{
if (!typeof(T).IsEnum)
{
throw new ArgumentException();
}
if (delimitedValues == string.Empty)
{
return new T[0];
}
string[] parts = delimitedValues.Split(',');
T[] converted = new T[parts.Length];
for (int i = 0; i < parts.Length; i++)
{
if (!Enum.TryParse(parts[i], out converted[i]))
{
throw new FormatException(parts[i]);
}
}
return converted;
}
public static TArray ConvertArray<TArray>(string delimitedValues) where TArray : IList
{
return MethodCache<TArray>.Convert(delimitedValues);
}
public static T?[] ConvertNullable<T>(string delimitedValues) where T : struct
{
if (!typeof(T).IsEnum)
{
throw new ArgumentException();
}
if (delimitedValues == string.Empty)
{
return new T?[0];
}
string[] parts = delimitedValues.Split(',');
T?[] converted = new T?[parts.Length];
for (int i = 0; i < parts.Length; i++)
{
if (parts[i] == string.Empty)
{
continue;
}
T value;
if (!Enum.TryParse(parts[i], out value))
{
throw new FormatException(parts[i]);
}
converted[i] = value;
}
return converted;
}
public static TArray ConvertNullableArray<TArray>(string delimitedValues) where TArray : IList
{
return MethodCache<TArray>.Convert(delimitedValues);
}
private static class MethodCache<TArray> where TArray : IList
{
public static readonly Func<string, TArray> Convert;
static MethodCache()
{
if (!typeof(TArray).IsArray)
{
throw new ArgumentException("TArray");
}
Type element = typeof(TArray).GetElementType();
Type element2 = Nullable.GetUnderlyingType(element);
if (element2 == null)
{
Convert = (Func<string, TArray>)Delegate.CreateDelegate(typeof(Func<string, TArray>), typeof(EnumSplitter).GetMethod("Convert").MakeGenericMethod(element));
}
else
{
Convert = (Func<string, TArray>)Delegate.CreateDelegate(typeof(Func<string, TArray>), typeof(EnumSplitter).GetMethod("ConvertNullable").MakeGenericMethod(element2));
}
}
}
}
我使用 MethodCache<>
class 来缓存对 "right" Convert<>
方法的反射调用。
使用:
TestValues[] arr1 = EnumSplitter.Convert<TestValues>("Unknown,Pizza");
TestValues?[] arr2 = EnumSplitter.ConvertNullable<TestValues>("Unknown,,Pizza,");
TestValues[] arr3 = EnumSplitter.ConvertArray<TestValues[]>("Unknown,Pizza");
TestValues?[] arr4 = EnumSplitter.ConvertNullableArray<TestValues?[]>("Unknown,,Pizza,");
public static T[] ToEnumsArray<T>(this string stringValuesWithDelimeter) where T : struct
{
var result = new List<T>();
if (!string.IsNullOrWhiteSpace(stringValuesWithDelimeter))
{
var arr = stringValuesWithDelimeter.Split(',');
foreach (var item in arr)
{
if (Enum.TryParse(item, true, out T enumResult))
{
result.Add(enumResult);
}
}
}
return result.ToArray();
}