2015-04-21 7 views
0

Мне нужно преобразовать строку с разделителями-запятыми в массив типов перечислений в универсальном классе.Преобразовать разделительную строку в enum массива с использованием общих типов в C# 2.0

Проблема в том, мне нужно создать массив на основе родового Т.

Вот мой код:

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 
} 

Любые идеи о том, как я могу это сделать? Я в тупике!

Я попытался создать массив объектов типа перечисления, но не могу показаться, чтобы преобразовать его в Т.

Пожалуйста, имейте в виду, это .Net Framwork 2,0 поэтому мой набор инструментов ограничен ,

Спасибо за любые идеи, которые любой может предоставить.

+0

Вы действительно не должны использовать 'var' за все, что собирается сделать это остов для отладки - кроме того, что по этому поводу решения не работает? –

+4

@EBrown 'var' не затрудняет отладку. – CodeCaster

+0

@CodeCaster Это вопрос мнения, в этой ** ситуации это может иметь минимальное влияние, но если он привык использовать 'var' всюду, он столкнется с нечетными проблемами в будущем, где это неопределенно почему решение не сработало. То есть Тип-несоответствия и т. Д. Да, это отлично подходит для анонимных типов, но определенно должно быть ** не ** быть по умолчанию «go-to» или «catch-all», когда он его использует. –

ответ

1

С минимальными изменениями кода: вы можете использовать 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); 
} 
+0

Это сработало! Не думал об использовании Array.CreateInstance. Спасибо за вашу помощь. –

7

Попробуйте это класс:

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; 
} 

Если вы хотите, вы можете даже иметь версию, которая будет возвращать T?[] (так что массив нулевых перечислений)

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,"); 

Обратите внимание, что эта последняя версия не пропускает «недопустимые» значения, она все равно бросает на них. Просто он преобразует string.Empty элементов в null. К сожалению, есть проблема: если вы ConvertNullable<TestValues>(string.Empty), он вернет 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<> класс для кэширования вызов отражения «право» 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,"); 
+1

Ницца! (Кстати, я думаю, вы можете опустить '' от 'if (! Enum.TryParse (parts [i], ...') –

+0

Согласен, все будет проще, если я смогу использовать T [], но в этом сценарий, я не могу изменить тип родового. Это образец, который я создал из части гораздо большего решения. Однако мне нравится ваш подход. –

 Смежные вопросы

  • Нет связанных вопросов^_^