Menyetel properti dengan refleksi dengan nilai string

312

Saya ingin menetapkan properti objek melalui Refleksi, dengan nilai tipe string. Jadi, misalnya, anggaplah saya memiliki Shipkelas, dengan properti Latitude, yang adalah a double.

Inilah yang ingin saya lakukan:

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

Seperti, ini melempar ArgumentException:

Objek bertipe 'System.String' tidak dapat dikonversi ke tipe 'System.Double'.

Bagaimana saya bisa mengkonversi nilai ke tipe yang tepat, berdasarkan propertyInfo?

David Hodgson
sumber
1
Pertanyaan untuk Anda: apakah ini bagian dari solusi ORM khusus?
user3308043

Jawaban:

527

Anda dapat menggunakan Convert.ChangeType()- Ini memungkinkan Anda untuk menggunakan informasi runtime pada IConvertiblejenis apa pun untuk mengubah format representasi. Namun, tidak semua konversi mungkin, dan Anda mungkin perlu menulis logika kasus khusus jika Anda ingin mendukung konversi dari jenis yang tidak IConvertible.

Kode yang sesuai (tanpa penanganan pengecualian atau logika kasus khusus) adalah:

Ship ship = new Ship();
string value = "5.5";
PropertyInfo propertyInfo = ship.GetType().GetProperty("Latitude");
propertyInfo.SetValue(ship, Convert.ChangeType(value, propertyInfo.PropertyType), null);
LBushkin
sumber
Tinjau jawaban @AliKaraca di bawah ini. Baik ini dan yang di bawah ini cepat dan longgar tetapi melakukan pekerjaan untuk tipe umum.
Aaron Hudon
Apakah ada TryChangeTypeatau CanChangeType?
Shimmy Weitzhandler
34

Seperti yang beberapa orang lain katakan, Anda ingin menggunakan Convert.ChangeType:

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

Bahkan, saya sarankan Anda melihat seluruh ConvertKelas .

Kelas ini, dan banyak kelas berguna lainnya adalah bagian dari SystemNamespace . Saya merasa berguna untuk memindai namespace itu setiap tahun atau lebih untuk melihat fitur apa yang saya lewatkan. Cobalah!

John Saunders
sumber
1
OP mungkin menginginkan jawaban umum, untuk menetapkan properti jenis apa pun yang memiliki konversi yang jelas dari sebuah string.
Daniel Earwicker
Poin yang bagus. Saya akan mengedit dan menunjuk ke penjawab yang sebenarnya, atau akan menghapus milik saya jika seseorang akan menambahkan apa yang saya katakan tentang sisa namespace.
John Saunders
19

Saya perhatikan banyak orang merekomendasikan Convert.ChangeType- Ini berfungsi untuk beberapa kasus namun segera setelah Anda mulai melibatkan nullablejenis Anda akan mulai menerima InvalidCastExceptions:

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

Sebuah pembungkus ditulis beberapa tahun yang lalu untuk menangani ini tetapi itu juga tidak sempurna.

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

Tablet
sumber
13

Saya mencoba jawaban dari LBushkin dan itu berhasil, tetapi itu tidak akan berfungsi untuk nilai nol dan bidang nullable. Jadi saya sudah mengubahnya menjadi ini:

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
sumber
Saya harus mengucapkan terima kasih ketika saya bertemu kasus ini dan ini adalah satu-satunya solusi. terima kasih ~!
Franva
11

Anda dapat menggunakan konverter tipe (tidak ada pengecekan error):

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

Dalam hal mengatur kode, Anda dapat membuat sejenis mixin yang akan menghasilkan kode seperti ini:

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

Ini akan dicapai dengan kode ini:

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 dapat digunakan kembali untuk berbagai kelas.

Anda juga dapat membuat konverter tipe khusus untuk dilampirkan ke properti atau kelas Anda:

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

[TypeConverter(typeof(LatitudeConverter))]
public class Latitude { ... }
Jordão
sumber
Apakah ada alasan khusus mengapa Anda menambahkan penanda muka bukan hanya menggunakan object?
Groo
1
Ya, antarmuka marker berfungsi sebagai pengganti untuk menambahkan metode ekstensi. Menggunakan objectakan menambahkan metode ekstensi ke semua kelas, yang umumnya tidak diinginkan.
Jordão
6

Anda mungkin mencari Convert.ChangeTypemetodenya. Sebagai contoh:

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
sumber
5

Menggunakan Convert.ChangeTypedan mendapatkan jenis yang akan dikonversi dari menu PropertyInfo.PropertyType.

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

Saya akan menjawab ini dengan jawaban umum. Biasanya jawaban ini tidak bekerja dengan panduan. Ini adalah versi yang berfungsi dengan panduan juga.

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
sumber
1
Ini harus menjadi jawaban yang diterima. Ini juga bekerja dengan GUID <3. Terima kasih, Ali (itu nama panggilan putriku)
Cătălin Rădoi
3

Atau Anda dapat mencoba:

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

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

Jika Anda menulis aplikasi Metro, Anda harus menggunakan kode lain:

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

catatan:

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

dari pada

ship.GetType().GetProperty("Latitude");
Serhiy
sumber
0

Menggunakan kode berikut akan menyelesaikan masalah Anda:

item.SetProperty(prop.Name, Convert.ChangeType(item.GetProperty(prop.Name).ToString().Trim(), prop.PropertyType));
Marco Sotto
sumber
-9

Apakah Anda ingin bermain-main dengan Reflection atau Anda ingin membuat perangkat lunak produksi? Saya akan mempertanyakan mengapa Anda menggunakan refleksi untuk menetapkan properti.

Double new_latitude;

Double.TryParse (value, out new_latitude);
ship.Latitude = new_latitude;
clemahieu
sumber
1
Anda harus menghargai apa yang orang coba lakukan dan bukan apa yang menurut Anda harus mereka lakukan. Diturunkan. (Dari GenericProgramming.exe:ReflectionBenefits())
Петър Петров