Подтвердить что ты не робот

С#: динамический анализ из System.Type

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

Можно ли каким-либо образом вызвать метод parse или преобразовать для этого типа в строку динамически?

В основном, как удалить инструкции if в этой логике

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);  
4b9b3361

Ответ 1

TypeDescriptor для спасения:

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

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

Ответ 2

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

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

EDIT: на самом деле в вашем случае вы не можете использовать дженерики (не так просто). Вместо этого вы можете сделать это:

object value = Convert.ChangeType(myString, propType);

Ответ 3

Я столкнулся с этой проблемой, и именно так я ее решил:

value = myString;
var parse = propType.GetMethod("Parse", new[] { typeof(string) });
if (parse != null) {
  value = parse.Invoke(null, new object[] { value });
}

... и это сработало для меня.

Подводя итог, вы пытаетесь найти статический метод "Parse" для типа объекта, который принимает только одну строку в качестве аргумента. Если вы найдете такой метод, тогда вызовите его с параметром string, который вы пытаетесь преобразовать. Поскольку p является PropertyInfo для моего типа, я закончил этот метод, установив свой экземпляр со значением следующим образом:

p.SetValue(instance, value, null);

Ответ 4

Зависит от того, что вы хотели бы выполнить.

1), если вы просто пытаетесь очистить свой код и удалите повторную проверку типов, то то, что вы хотите сделать, - это централизовать ваши проверки в методе, comme

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), если вы хотите что-то более обобщенное для базовых типов, вы можете попробовать что-то вроде Thomas Levesque предлагает - хотя, по правде говоря, я сам этого не делал, я не знаком с [последними?] расширениями до Convert. Также очень хорошее предложение.

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

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

Конечно, все эти методы делают практически одно и то же. В любом из них нет никакой магии, в какой-то момент кто-то выполняет линейный поиск [будь то неявный поиск через последовательные 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 или другой сложный формат строки.

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

Ответ 5

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

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

  • строка, подлежащая анализу
  • тип, используемый для синтаксического анализа.

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

Ответ 6

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

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