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?

David Hodgson
fonte
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);
LBushkin
fonte
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!

John Saunders
fonte
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

Tábua
fonte
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);
}
Ashkan Sirous
fonte
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 { ... }
Jordão
fonte
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);
John Calsbeek
fonte
5

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

propertyInfo.SetValue( ship,
                       Convert.ChangeType( value, propertyInfo.PropertyType ),
                       null );
tvanfosson
fonte
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); 
Ali Karaca
fonte
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...
bytebender
fonte
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");
Serhiy
fonte
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));
Marco Sotto
fonte
-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;
clemahieu
fonte
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())
Петър Петров