การตั้งค่าคุณสมบัติโดยการสะท้อนกับค่าสตริง


312

stringฉันต้องการที่จะตั้งค่าคุณสมบัติของวัตถุผ่านสะท้อนที่มีค่าของประเภท ดังนั้นสำหรับตัวอย่างเช่นสมมติว่าฉันมีShipระดับด้วยคุณสมบัติของซึ่งเป็นLatitudedouble

นี่คือสิ่งที่ฉันต้องการจะทำ:

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

เช่นนี้จะพ่นArgumentException:

วัตถุประเภท 'System.String' ไม่สามารถแปลงเป็นประเภท 'System.Double'

ฉันจะแปลงค่าเป็นประเภทที่เหมาะสมได้propertyInfoอย่างไร


1
คำถามสำหรับคุณ: เป็นส่วนหนึ่งของโซลูชัน ORM ที่กำหนดเองหรือไม่
user3308043

คำตอบ:


527

คุณสามารถใช้Convert.ChangeType()- ช่วยให้คุณสามารถใช้ข้อมูลรันไทม์บนIConvertibleประเภทใดก็ได้เพื่อเปลี่ยนรูปแบบการแสดง IConvertibleแปลงไม่ได้ทั้งหมดที่เป็นไปได้แม้ว่าและคุณอาจจะต้องเขียนเหตุผลกรณีพิเศษหากคุณต้องการการสนับสนุนจากการแปลงประเภทที่ไม่ได้

รหัสที่เกี่ยวข้อง (โดยไม่มีการจัดการข้อยกเว้นหรือตรรกะกรณีพิเศษ) จะเป็น:

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

อ่านคำตอบ @AliKaraca ด้านล่าง ทั้งสิ่งนี้และสิ่งที่อยู่ด้านล่างนั้นเร็วและหลวม แต่ทำงานให้กับประเภททั่วไป
Aaron Hudon

มีTryChangeTypeหรือCanChangeType?
Shimmy Weitzhandler

34

อย่างที่หลายคนพูดคุณต้องการใช้Convert.ChangeType:

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

ในความเป็นจริงผมขอแนะนำให้คุณมองไปที่ทั้งชั้นConvert

ชั้นนี้และชั้นเรียนที่มีประโยชน์อื่น ๆ อีกมากมายเป็นส่วนหนึ่งของNamespaceSystem ฉันพบว่ามีประโยชน์ในการสแกนเนมสเปซนั้นเป็นประจำทุกปีหรือมากกว่านั้นเพื่อดูว่าฉันพลาดคุณลักษณะใดไปบ้าง ให้มันลอง!


1
OP อาจต้องการคำตอบทั่วไปสำหรับการตั้งค่าคุณสมบัติประเภทใด ๆ ที่มีการแปลงที่ชัดเจนจากสตริง
Daniel Earwicker

จุดดี. ฉันจะแก้ไขและชี้ไปที่ผู้ตอบที่แท้จริงหรือจะลบของฉันถ้ามีคนจะเพิ่มสิ่งที่ฉันพูดเกี่ยวกับส่วนที่เหลือของ namespace
จอห์นแซนเดอ

19

ฉันสังเกตเห็นว่ามีผู้คนมากมายที่แนะนำConvert.ChangeType- วิธีนี้ใช้ได้ผลในบางกรณี แต่ทันทีที่คุณเริ่มเกี่ยวข้องกับnullableประเภทที่คุณจะเริ่มได้รับInvalidCastExceptions:

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

เสื้อคลุมถูกเขียนเมื่อไม่กี่ปีที่ผ่านมาเพื่อจัดการเรื่องนี้ แต่นั่นก็ไม่สมบูรณ์แบบเช่นกัน

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


13

ฉันลองคำตอบจากLBushkinและใช้งานได้ดี แต่มันจะไม่ทำงานสำหรับค่า Null และเขตข้อมูล null ดังนั้นฉันจึงเปลี่ยนเป็น:

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

ฉันต้องขอขอบคุณเมื่อพบกรณีนี้และนี่เป็นทางออกเดียวเท่านั้น ขอบคุณ ~!
Franva

11

คุณสามารถใช้ตัวแปลงชนิด (ไม่มีการตรวจสอบข้อผิดพลาด):

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

ในแง่ของการจัดระเบียบโค้ดคุณสามารถสร้างมิกซ์อินประเภทที่จะทำให้เกิดโค้ดดังนี้:

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

นี่จะสำเร็จได้ด้วยรหัสนี้:

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 สามารถนำมาใช้ซ้ำสำหรับคลาสที่แตกต่างกัน

คุณยังสามารถสร้างตัวแปลงประเภทที่คุณกำหนดเองเพื่อแนบไปกับคุณสมบัติหรือคลาสของคุณ:

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

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

มีเหตุผลใดบ้างที่ทำให้คุณเพิ่มเครื่องหมายแทนที่หน้าเครื่องหมายแทนที่จะใช้เพียงอย่างเดียวobject?
Groo

1
ใช่อินเทอร์เฟซของตัวทำเครื่องหมายทำหน้าที่เป็นตัวยึดตำแหน่งเพื่อเพิ่มวิธีการส่วนขยาย การใช้objectจะเพิ่มเมธอดส่วนขยายให้กับคลาสทั้งหมดซึ่งโดยทั่วไปไม่ต้องการ
Jordão

6

คุณอาจกำลังมองหาConvert.ChangeTypeวิธีการ ตัวอย่างเช่น:

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

5

การใช้และได้รับประเภทที่จะแปลงจากConvert.ChangeTypePropertyInfo.PropertyType

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

4

ฉันจะตอบคำถามนี้พร้อมคำตอบทั่วไป คำตอบเหล่านี้มักจะไม่ทำงานกับ guids นี่คือเวอร์ชันการทำงานที่มี guids ด้วย

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
นี่ควรเป็นคำตอบที่ยอมรับได้ นอกจากนี้ยังใช้งานได้กับ GUIDs <3 ขอบคุณ Ali (นั่นคือชื่อเล่นลูกสาวของฉัน)
CătălinRădoi

3

หรือคุณสามารถลอง:

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

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

2

หากคุณกำลังเขียนแอพ Metro คุณควรใช้รหัสอื่น:

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

บันทึก:

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

แทน

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

0

การใช้รหัสต่อไปนี้ควรแก้ปัญหาของคุณ:

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

-9

คุณกำลังมองหาสิ่งใหม่ ๆ กับ Reflection หรือคุณต้องการสร้างซอฟต์แวร์ที่ใช้งานจริงหรือ? ฉันจะถามว่าทำไมคุณถึงใช้การสะท้อนเพื่อตั้งค่าคุณสมบัติ

Double new_latitude;

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

1
คุณควรเคารพสิ่งที่ผู้คนพยายามทำและไม่ใช่สิ่งที่คุณคิดว่าพวกเขาต้องทำ downvoted (จากGenericProgramming.exe:ReflectionBenefits())
ПетърПетров
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.