C#: Динамический синтаксический анализ от Системы. Ввести

У меня есть Тип, Строка и Объект.

Есть ли некоторый способ, которым я могу назвать метод синтаксического анализа или преобразовать для того типа на строке динамично?

В основном, как я удаляю если операторы в этой логике

object value = new object();    
String myString = "something";
Type propType = p.PropertyType;

if(propType == Type.GetType("DateTime"))
{
    value = DateTime.Parse(myString);
}

if (propType == Type.GetType("int"))
{
    value = int.Parse(myString);
}

И сделайте что-то больше как это.

object value = new object();
String myString = "something";
Type propType = p.PropertyType;


//this doesn't actually work
value = propType .Parse(myString);  
47
задан David Pfeffer 4 March 2010 в 15:45
поделиться

5 ответов

TypeDescriptor в помощь:

var converter = TypeDescriptor.GetConverter(propType);
var result = converter.ConvertFrom(myString);

Для интеграции в инфраструктуру TypeConverter реализуйте свой собственный TypeConverter и украсьте класс, который будет преобразован с его помощью, TypeConverterAttribute

80
ответ дан 26 November 2019 в 19:32
поделиться

Это должно работать для всех примитивных типов, а также для типов, реализующих IConvertible

public static T ConvertTo<T>(object value)
{
    return (T)Convert.ChangeType(value, typeof(T));
}

РЕДАКТИРОВАТЬ: на самом деле в вашем случае вы не можете использовать дженерики (по крайней мере, непросто). Вместо этого вы можете сделать это:

object value = Convert.ChangeType(myString, propType);
15
ответ дан 26 November 2019 в 19:32
поделиться

Зависит от того, чего вы хотите достичь.

1) если вы просто пытаетесь очистить свой код и удалить повторяющуюся проверку типов, то вам нужно централизовать свои проверки в методе, com

public static T To<T> (this string stringValue)
{
    T value = default (T);

    if (typeof (T) == typeof (DateTime))
    {
        // insert custom or convention System.DateTime 
        // deserialization here ...
    }
    // ... add other explicit support here
    else
    {
        throw new NotSupportedException (
            string.Format (
            "Cannot convert type [{0}] with value [{1}] to type [{2}]." + 
            " [{2}] is not supported.",
            stringValue.GetType (),
            stringValue,
            typeof (T)));
    }

    return value;
}

2) если вы хотите что-то более обобщенное для базовые типы, вы можете попробовать что-то вроде Томас Левеск предлагает - хотя, по правде говоря, я сам не пробовал этого, я не знаком с [недавними?] расширениями для Convert . Также очень хорошее предложение.

3) на самом деле, вы, вероятно, захотите объединить пункты 1) и 2) выше в одно расширение, которое позволит вам поддерживать базовое преобразование значений и явную поддержку сложных типов.

4) если вы хотите быть полностью «свободными от рук», вы также можете по умолчанию использовать простую старую десериализацию [Xml или двоичный, либо / или].Конечно, это ограничивает ваш ввод - т.е. все вводимые данные должны быть в соответствующем формате Xml или двоичном формате. Честно говоря, это, наверное, перебор, но стоит упомянуть.

Конечно, все эти методы, по сути, делают одно и то же. Ни в одном из них нет никакого волшебства, в какой-то момент кто-то выполняет линейный поиск [будь то неявный поиск с помощью последовательных предложений if или скрытый с помощью средств преобразования и сериализации .Net].

5) если вы хотите повысить производительность, то вам нужно улучшить «поисковую» часть процесса преобразования. Создайте явный список «поддерживаемых типов», каждый тип которого соответствует индексу в массиве. Вместо того, чтобы указывать Тип при вызове, вы затем указываете индекс.

РЕДАКТИРОВАТЬ: поэтому, хотя линейный поиск является аккуратным и быстрым, мне также приходит в голову, что было бы еще быстрее, если бы потребитель просто получил функции преобразования и вызвал их напрямую. То есть потребитель знает, в какой тип он хотел бы преобразовать [это задано], поэтому, если ему нужно преобразовать много элементов одновременно,

// S == source type
// T == target type
public interface IConvert<S>
{
    // consumers\infrastructure may now add support
    int AddConversion<T> (Func<S, T> conversion);

    // gets conversion method for local consumption
    Func<S, T> GetConversion<T> ();

    // easy to use, linear look up for one-off conversions
    T To<T> (S value);
}

public class Convert<S> : IConvert<S>
{

    private class ConversionRule
    {
        public Type SupportedType { get; set; }
        public Func<S, object> Conversion { get; set; }
    }

    private readonly List<ConversionRule> _map = new List<ConversionRule> ();
    private readonly object _syncRoot = new object ();

    public void AddConversion<T> (Func<S, T> conversion)
    {
        lock (_syncRoot)
        {
            if (_map.Any (c => c.SupportedType.Equals (typeof (T))))
            {
                throw new ArgumentException (
                    string.Format (
                    "Conversion from [{0}] to [{1}] already exists. " +
                    "Cannot add new conversion.", 
                    typeof (S), 
                    typeof (T)));
            }

            ConversionRule conversionRule = new ConversionRule
            {
                SupportedType = typeof(T),
                Conversion = (s) => conversion (s),
            };
            _map.Add (conversionRule);
        }
    }

    public Func<S, T> GetConversion<T> ()
    {
        Func<S, T> conversionMethod = null;

        lock (_syncRoot)
        {
            ConversionRule conversion = _map.
                SingleOrDefault (c => c.SupportedType.Equals (typeof (T)));

            if (conversion == null)
            {
                throw new NotSupportedException (
                    string.Format (
                    "Conversion from [{0}] to [{1}] is not supported. " + 
                    "Cannot get conversion.", 
                    typeof (S), 
                    typeof (T)));
            }

            conversionMethod = 
                (value) => ConvertWrap<T> (conversion.Conversion, value);
        }

        return conversionMethod;
    }

    public T To<T> (S value)
    {
        Func<S, T> conversion = GetConversion<T> ();
        T typedValue = conversion (value);
        return typedValue;
    }

    // private methods

    private T ConvertWrap<T> (Func<S, object> conversion, S value)
    {
        object untypedValue = null;
        try
        {
            untypedValue = conversion (value);
        }
        catch (Exception exception)
        {
            throw new ArgumentException (
                string.Format (
                "Unexpected exception encountered during conversion. " +
                "Cannot convert [{0}] [{1}] to [{2}].",
                typeof (S),
                value,
                typeof (T)),
                exception);
        }

        if (!(untypedValue is T))
        {
            throw new InvalidCastException (
                string.Format (
                "Converted [{0}] [{1}] to [{2}] [{3}], " +
                "not of expected type [{4}]. Conversion failed.",
                typeof (S),
                value,
                untypedValue.GetType (),
                untypedValue,
                typeof (T)));
        }

        T typedValue = (T)(untypedValue);

        return typedValue;
    }

}

, и он будет использоваться как

// as part of application innitialization
IConvert<string> stringConverter = container.Resolve<IConvert<string>> ();
stringConverter.AddConversion<int> (s => Convert.ToInt32 (s));
stringConverter.AddConversion<Color> (s => CustomColorParser (s));

...

// a consumer elsewhere in code, say a Command acting on 
// string input fields of a form
// 
// NOTE: stringConverter could be injected as part of DI
// framework, or obtained directly from IoC container as above
int someCount = stringConverter.To<int> (someCountString);

Func<string, Color> ToColor = stringConverter.GetConversion <Color> ();
IEnumerable<Color> colors = colorStrings.Select (s => ToColor (s));

, я предпочитаю этот последний подход. , потому что это дает вам полный контроль над преобразованием. Если вы используете контейнер Inversion of Control [IoC], такой как Castle Windsor или Unity, то внедрение этой службы будет сделано за вас. Кроме того, поскольку он основан на экземпляре , у вас может быть несколько экземпляров, каждый со своим собственным набором правил преобразования - например, если у вас есть несколько пользовательских элементов управления, каждый из которых генерирует свой собственный DateTime или другой сложный строковый формат.

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

2
ответ дан 26 November 2019 в 19:32
поделиться

Технически невозможно посмотреть на строку и точно знать, какой тип она представляет.

Итак, для любого универсального подхода вам потребуется как минимум:

  1. строка для анализа;
  2. тип, используемый для анализа.

Взгляните на статический метод Convert.ChangeType () .

0
ответ дан 26 November 2019 в 19:32
поделиться

Похоже, что то, что вы хотите сделать (по крайней мере, если задействованные типы являются типами, для которых вы не можете изменить исходный текст), потребует утиной типизации, которой нет в C#

Если вам нужно делать это часто, я бы обернул логику в класс или метод, которому вы можете передать "myString" и "propType", и он вернет значение. В этом методе вы просто выполните цепочку if, описанную выше, и вернете значение, когда оно найдет совпадение. Вам все равно придется вручную перечислять все возможные типы, но делать это нужно будет только один раз.

-1
ответ дан 26 November 2019 в 19:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: