Definindo uma propriedade por reflexão com um valor de sequência


312

Eu gostaria de definir uma propriedade de um objeto através da reflexão, com um valor do tipo string. Então, por exemplo, suponha que eu tenha uma Shipclasse, com uma propriedade de Latitude, que é a double.

Aqui está o que eu gostaria de fazer:

Ship ship = new Ship();
string value = "5.5";
PropertyInfo propertyInfo = ship.GetType().GetProperty("Latitude");
propertyInfo.SetValue(ship, value, null);

Como é, isso gera um ArgumentException:

O objeto do tipo 'System.String' não pode ser convertido para o tipo 'System.Double'.

Como posso converter valor para o tipo adequado, com base em propertyInfo?


1
Pergunta para você: isso faz parte de uma solução ORM personalizada?
user3308043

Respostas:


527

Você pode usar Convert.ChangeType()- Permite usar informações de tempo de execução em qualquer IConvertibletipo para alterar os formatos de representação. Porém, nem todas as conversões são possíveis e você pode precisar escrever uma lógica de caso especial se desejar oferecer suporte a conversões de tipos que não são IConvertible.

O código correspondente (sem manipulação de exceção ou lógica de caso especial) seria:

Ship ship = new Ship();
string value = "5.5";
PropertyInfo propertyInfo = ship.GetType().GetProperty("Latitude");
propertyInfo.SetValue(ship, Convert.ChangeType(value, propertyInfo.PropertyType), null);

Reveja a resposta @AliKaraca abaixo. Tanto este como o abaixo são rápidos e soltos, mas fazem o trabalho para tipos comuns.
Aaron Hudon

Existe um TryChangeTypeou CanChangeType?
Shimmy Weitzhandler

34

Como vários outros disseram, você deseja usar Convert.ChangeType:

propertyInfo.SetValue(ship,
    Convert.ChangeType(value, propertyInfo.PropertyType),
    null);

Na verdade, eu recomendo que você olhe para toda a Convertclasse .

Esta classe e muitas outras classes úteis fazem parte do Systemespaço para nome . Acho útil verificar esse namespace todos os anos para ver quais recursos perdi. De uma chance!


1
O OP provavelmente quer a resposta geral, para definir uma propriedade de qualquer tipo que tenha uma conversão óbvia de uma string.
21119 Daniel Earwicker

Bom ponto. Vou editar e apontar para os respondentes reais ou excluirá os meus se alguém adicionar o que eu disse sobre o restante do espaço para nome.
317 John Saunders

19

Percebo que muitas pessoas estão recomendando Convert.ChangeType- Isso funciona em alguns casos, no entanto, assim que você começa a envolver os nullabletipos, começa a receber InvalidCastExceptions:

http://weblogs.asp.net/pjohnson/archive/2006/02/07/Convert.ChangeType-doesn_2700_t-handle-nullables.aspx

Um invólucro foi escrito há alguns anos para lidar com isso, mas isso também não é perfeito.

http://weblogs.asp.net/pjohnson/archive/2006/02/07/Convert.ChangeType-doesn_2700_t-handle-nullables.aspx


13

Eu tentei a resposta do LBushkin e funcionou muito bem, mas não funcionará para valores nulos e campos anuláveis. Então, eu mudei para isso:

propertyName= "Latitude";
PropertyInfo propertyInfo = ship.GetType().GetProperty(propertyName);
if (propertyInfo != null)
{
     Type t = Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType;
     object safeValue = (value == null) ? null : Convert.ChangeType(value, t);
     propertyInfo.SetValue(ship, safeValue, null);
}

Devo dizer obrigado quando conheci este caso e esta é a única solução. obrigado ~!
Franva

11

Você pode usar um conversor de tipos (sem verificação de erros):

Ship ship = new Ship();
string value = "5.5";
var property = ship.GetType().GetProperty("Latitude");
var convertedValue = property.Converter.ConvertFrom(value);
property.SetValue(self, convertedValue);

Em termos de organização do código, você pode criar um tipo de mixin que resultaria em código como este:

Ship ship = new Ship();
ship.SetPropertyAsString("Latitude", "5.5");

Isso seria alcançado com este código:

public interface MPropertyAsStringSettable { }
public static class PropertyAsStringSettable {
  public static void SetPropertyAsString(
    this MPropertyAsStringSettable self, string propertyName, string value) {
    var property = TypeDescriptor.GetProperties(self)[propertyName];
    var convertedValue = property.Converter.ConvertFrom(value);
    property.SetValue(self, convertedValue);
  }
}

public class Ship : MPropertyAsStringSettable {
  public double Latitude { get; set; }
  // ...
}

MPropertyAsStringSettable pode ser reutilizado para muitas classes diferentes.

Você também pode criar seus próprios conversores de tipo personalizado para anexar às suas propriedades ou classes:

public class Ship : MPropertyAsStringSettable {
  public Latitude Latitude { get; set; }
  // ...
}

[TypeConverter(typeof(LatitudeConverter))]
public class Latitude { ... }

Existe algum motivo específico para você adicionar o marcador na interface em vez de apenas usá-lo object?
Groo

1
Sim, a interface do marcador serve como um espaço reservado para adicionar os métodos de extensão. Usar objectadicionaria os métodos de extensão a todas as classes, o que geralmente não é desejável.
Jordão

6

Você provavelmente está procurando o Convert.ChangeTypemétodo. Por exemplo:

Ship ship = new Ship();
string value = "5.5";
PropertyInfo propertyInfo = ship.GetType().GetProperty("Latitude");
propertyInfo.SetValue(ship, Convert.ChangeType(value, propertyInfo.PropertyType), null);

5

Usando Convert.ChangeTypee obtendo o tipo para converter do PropertyInfo.PropertyType.

propertyInfo.SetValue( ship,
                       Convert.ChangeType( value, propertyInfo.PropertyType ),
                       null );

4

Vou responder isso com uma resposta geral. Geralmente essas respostas não funcionam com guias. Aqui está uma versão de trabalho com guias também.

var stringVal="6e3ba183-89d9-e611-80c2-00155dcfb231"; // guid value as string to set
var prop = obj.GetType().GetProperty("FooGuidProperty"); // property to be setted
var propType = prop.PropertyType;

// var will be type of guid here
var valWithRealType = TypeDescriptor.GetConverter(propType).ConvertFrom(stringVal); 

1
Essa deve ser a resposta aceita. Também funciona com GUIDs <3. Obrigado, Ali (esse é o apelido da minha filha)
Cătălin Rădoi

3

Ou você pode tentar:

propertyInfo.SetValue(ship, Convert.ChangeType(value, propertyInfo.PropertyType), null);

//But this will cause problems if your string value IsNullOrEmplty...

2

Se você estiver escrevendo o aplicativo Metro, use outro código:

Ship ship = new Ship();
string value = "5.5";
PropertyInfo propertyInfo = ship.GetType().GetTypeInfo().GetDeclaredProperty("Latitude");
propertyInfo.SetValue(ship, Convert.ChangeType(value, propertyInfo.PropertyType));

Nota:

ship.GetType().GetTypeInfo().GetDeclaredProperty("Latitude");

ao invés de

ship.GetType().GetProperty("Latitude");

0

O uso do código a seguir deve resolver seu problema:

item.SetProperty(prop.Name, Convert.ChangeType(item.GetProperty(prop.Name).ToString().Trim(), prop.PropertyType));

-9

Você deseja brincar com o Reflection ou criar um software de produção? Gostaria de perguntar por que você está usando reflexão para definir uma propriedade.

Double new_latitude;

Double.TryParse (value, out new_latitude);
ship.Latitude = new_latitude;

1
Você deve respeitar o que as pessoas tentam fazer e não o que você acha que elas devem fazer. Votado. (From GenericProgramming.exe:ReflectionBenefits())
Петър Петров
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.