C #: análise dinâmica de System.Type

Eu tenho um tipo, uma string e um object.

Existe alguma maneira eu posso chamar o método de análise ou converter para esse tipo na seqüência de caracteres dinamicamente?

Basicamente como faço para remover as instruções if nesta lógica

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

E faça mais assim.

 object value = new object(); String myString = "something"; Type propType = p.PropertyType; //this doesn't actually work value = propType .Parse(myString); 

TypeDescriptor para resgatar:

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

Para integrar-se à infraestrutura TypeConverter , implemente sua própria class TypeConverter e decorate para ser convertida com ela com TypeConverterAttribute

Isso deve funcionar para todos os tipos primitivos e para os tipos que implementam o IConvertible

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

EDIT: na verdade, no seu caso, você não pode usar genéricos (pelo menos não facilmente). Em vez disso, você poderia fazer isso:

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

Eu corri para este problema e é assim que eu resolvi isso:

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

… e funcionou para mim.

Para resumir, você está tentando encontrar um método “Parse” estático no tipo de object que usa apenas uma string como argumento. Se você encontrar esse método, chame-o com o parâmetro de string que você está tentando converter. Como p é o PropertyInfo para o meu tipo, eu terminei esse método definindo minha instância com o valor da seguinte forma:

 p.SetValue(instance, value, null); 

Depende do que você gostaria de realizar.

1) se você está simplesmente tentando limpar seu código e remover a verificação de tipo repetitivo, então o que você quer fazer é centralizar seus cheques em um método, comme

 public static T To (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) Se você gostaria de algo mais generalizado para tipos básicos, você poderia tentar algo como sugere Thomas Levesque – embora, na verdade, eu não tentei isso sozinho, eu não estou familiarizado com [recentes?] Extensões para Convert . Também uma sugestão muito boa.

3) na verdade, você provavelmente deseja mesclar os itens 1) e 2) acima em uma única extensão que permitiria o suporte à conversão de valor básico e suporte a tipo complexo explícito.

4) se você quiser ser completamente “hands-free”, então você também pode usar por padrão o Deserialization [Xml or Binary, or / or]. Claro, isso restringe sua input – ou seja, todas as inputs devem estar em um formato Xml ou Binário apropriado. Honestamente, isso é provavelmente um exagero, mas vale a pena mencionar.

Naturalmente, todos esses methods fazem essencialmente a mesma coisa. Não há mágica em nenhum deles, em algum momento alguém está realizando uma pesquisa linear [se é um olhar implícito através de cláusulas if seqüenciais ou sob o capô via facilidades de conversão e serialização .Net].

5) se você quiser melhorar o desempenho, o que você deseja fazer é melhorar a parte de “pesquisa” do processo de conversão. Crie uma lista explícita de “tipos suportados”, cada tipo correspondendo a um índice em uma matriz. Em vez de especificar o tipo em uma chamada, você especifica o índice.

EDIT: assim, enquanto look up linear é puro e rápido, também me ocorre que seria ainda mais rápido se o consumidor simplesmente obtivesse funções de conversão e as chamasse diretamente. Ou seja, o consumidor sabe de que tipo gostaria de converter para [isso é um dado], portanto, se precisar converter muitos itens de uma só vez,

 // S == source type // T == target type public interface IConvert { // consumers\infrastructure may now add support int AddConversion (Func conversion); // gets conversion method for local consumption Func GetConversion (); // easy to use, linear look up for one-off conversions T To (S value); } public class Convert : IConvert { private class ConversionRule { public Type SupportedType { get; set; } public Func Conversion { get; set; } } private readonly List _map = new List (); private readonly object _syncRoot = new object (); public void AddConversion (Func 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 GetConversion () { Func 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 (conversion.Conversion, value); } return conversionMethod; } public T To (S value) { Func conversion = GetConversion (); T typedValue = conversion (value); return typedValue; } // private methods private T ConvertWrap (Func 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; } } 

e seria usado como

 // as part of application innitialization IConvert stringConverter = container.Resolve> (); stringConverter.AddConversion (s => Convert.ToInt32 (s)); stringConverter.AddConversion (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 (someCountString); Func ToColor = stringConverter.GetConversion  (); IEnumerable colors = colorStrings.Select (s => ToColor (s)); 

Eu prefiro muito mais esta última abordagem, porque ela oferece controle total sobre a conversão. Se você usar um container Inversion of Control [IoC] como Castle Windsor ou Unity, a injeção deste serviço será feita para você. Além disso, como é baseado em instâncias , você pode ter várias instâncias, cada uma com seu próprio conjunto de regras de conversão – se, por exemplo, você tiver vários controles de usuário, cada um gerando seu próprio DateTime ou outro formato de string complexo.

Poxa, mesmo se você quisesse suportar múltiplas regras de conversão para um único tipo de destino, isso também é possível, você simplesmente tem que estender os parâmetros do método para especificar qual deles.

É tecnicamente impossível observar uma string e saber com certeza qual tipo ela representa.

Portanto, para qualquer abordagem genérica, você precisará de pelo menos:

  1. a string a ser analisada
  2. o tipo usado para análise.

Dê uma olhada no método estático Convert.ChangeType() .

Parece que o que você quer fazer (pelo menos, se os tipos envolvidos são tipos para os quais você não pode modificar a fonte) exigiria a digitação de pato que não está em C #

Se você precisa fazer muito isso, eu colocaria a lógica em uma class ou método que você pode passar “myString” e “propType” para e retornaria valor. Nesse método, basta fazer a cadeia if que você tem acima e retornar o valor quando encontrar um que corresponda. Você teria que listar manualmente todos os tipos possíveis ainda, mas você teria que fazer isso apenas uma vez.