การแปลงประเภททั่วไปจากสตริง


234

ฉันมีคลาสที่ฉันต้องการใช้เพื่อเก็บ "คุณสมบัติ" สำหรับคลาสอื่น คุณสมบัติเหล่านี้มีชื่อและค่า นึกคิดสิ่งที่ฉันต้องการคือการสามารถพิมพ์เพิ่มคุณสมบัติที่เพื่อให้ "ค่า" ที่ส่งคืนนั้นเป็นประเภทที่ฉันต้องการ

ประเภทควรเป็นแบบดั้งเดิม คลาสนี้คลาสย่อยเป็นคลาสนามธรรมซึ่งโดยทั่วไปจะเก็บชื่อและค่าเป็นสตริง แนวคิดที่ว่าคลาสย่อยนี้จะเพิ่มความปลอดภัยบางประเภทให้กับคลาสฐาน (รวมทั้งช่วยฉันในการแปลงบางส่วน)

ดังนั้นฉันได้สร้างชั้นเรียนซึ่งเป็น (ประมาณ) นี้:

public class TypedProperty<DataType> : Property
{
    public DataType TypedValue
    {
        get { // Having problems here! }
        set { base.Value = value.ToString();}
    }
}

ดังนั้นคำถามคือ:

มีวิธี "ทั่วไป" ในการแปลงจากสตริงกลับเป็นดั้งเดิม?

ฉันไม่พบอินเทอร์เฟซทั่วไปที่เชื่อมโยงการแปลงทั่วกระดาน (เช่นITryParsableน่าจะเหมาะอย่างยิ่ง!)


ฉันสนใจที่จะเห็นตัวอย่างของคลาสคอนกรีตของคุณแม้แต่ตัวอย่าง :)
Jon Limjap

คุณช่วยกรุณาโพสต์ชิ้นส่วนที่เกี่ยวข้องของคลาสพื้นฐานของคุณได้หรือไม่?
JJS

ฉันสงสัยว่าทุกคนสามารถหาคำตอบได้ที่นี่ทำงานใน. Net Standard 1.2: /
Dan Rayson

คำตอบ:


374

ฉันไม่แน่ใจว่าฉันเข้าใจความตั้งใจของคุณถูกต้องหรือไม่ แต่มาดูกันว่าสิ่งนี้ช่วย

public class TypedProperty<T> : Property where T : IConvertible
{
    public T TypedValue
    {
        get { return (T)Convert.ChangeType(base.Value, typeof(T)); }
        set { base.Value = value.ToString();}
    }
}

ฉันสงสัยอยู่สองสามวันว่าจะเลิกทำการสตรีมเป็นประเภททั่วไปได้อย่างไร ขอบคุณ :)
กับดัก

3
ฉันเห็นด้วยแม้ว่าConvert.ChangeTypeจะไม่ใช่วิธีที่เป็นสากลและขยายได้ แต่ก็ใช้ได้กับประเภทพื้นฐานส่วนใหญ่ หากจำเป็นต้องมีสิ่งที่ดีกว่าก็ไม่มีปัญหาที่จะรวมวิธีนี้เป็นสิ่งที่ใหญ่กว่าอย่างที่ Tim แนะนำหรือใช้วิธีการแปลงที่แตกต่างกันโดยสิ้นเชิง
lubos hasko

18
ฉันจะเพิ่มที่ T: IConvertible แน่นอน
MikeT

5
Type T ไม่ควร IConvertible แต่ Type of base มูลค่าควรเป็น
chapluck

74

วิธีการของ lubos hasko ล้มเหลวสำหรับ nullables วิธีการด้านล่างจะใช้งานได้สำหรับ nullables ฉันไม่ได้คิดเรื่องนี้ ฉันพบมันผ่าน Google: http://web.archive.org/web/20101214042641/http://dogaoztuzun.com/post/C-Generic-Type-Conversion.aspxเครดิตเป็น "Tuna Toksoz"

การใช้งานครั้งแรก:

TConverter.ChangeType<T>(StringValue);  

ชั้นเรียนอยู่ด้านล่าง

public static class TConverter
{
    public static T ChangeType<T>(object value)
    {
        return (T)ChangeType(typeof(T), value);
    }

    public static object ChangeType(Type t, object value)
    {
        TypeConverter tc = TypeDescriptor.GetConverter(t);
        return tc.ConvertFrom(value);
    }

    public static void RegisterTypeConverter<T, TC>() where TC : TypeConverter
    {

        TypeDescriptor.AddAttributes(typeof(T), new TypeConverterAttribute(typeof(TC)));
    }
}

ฉันจะเพิ่มตัวเลือกการแปลงทางเลือกสำหรับ Enums และโครงสร้างที่ซับซ้อนอื่น ๆ แต่โทรดี
Tomer W

2
ทำไมต้อง RegisterTypeConverter เราจำเป็นต้องลงทะเบียนตัวแปลงก่อนมือหรือไม่ (น่าเสียดายที่ลิงค์นั้นตายไปแล้วดังนั้นฉันจึงอ่านมันไม่ออก)
โคเฮน

สำหรับการแปลงหลายครั้งคุณควรสร้างtc(ครั้งTypeConverter) หนึ่งครั้งเท่านั้น ช้าเพราะใช้การสะท้อนเพื่อค้นหาTypeConverter TypeConverterAttributeหากคุณกำหนดค่าเริ่มต้นให้กับTypeConverterเขตข้อมูลส่วนตัวเดียวคุณควรจะสามารถใช้ซ้ำได้TypeConverterหลายครั้ง
Kevin P. Rice

ทำงานได้ดี แต่ถ้า T เป็นobjectข้อผิดพลาด ฉันสามารถแก้ไขได้โดยใช้ `if (typeof (T) .IsPrimitive) {return TConverter.ChangeType <T> (StringValue); } else {object o = (object) StringValue; กลับไปที่; } `เพื่อทดแทนสำหรับตัวอย่างการใช้งาน TConverter.ChangeType<T>(StringValue)
Matt

14

สำหรับหลายประเภท (จำนวนเต็มสองเท่า DateTime และอื่น ๆ ) มีวิธีการวิเคราะห์คำแบบคงที่ คุณสามารถเรียกใช้โดยใช้การสะท้อน:

MethodInfo m = typeof(T).GetMethod("Parse", new Type[] { typeof(string) } );

if (m != null)
{
    return m.Invoke(null, new object[] { base.Value });
}

8
TypeDescriptor.GetConverter(PropertyObject).ConvertFrom(Value)

TypeDescriptorเป็นคลาสที่มีวิธีการGetConvertorซึ่งยอมรับTypeวัตถุและจากนั้นคุณสามารถเรียกConvertFromวิธีการแปลงvalueสำหรับวัตถุที่ระบุนั้น


ฉันเองคิดว่าส่วนต่อประสานนี้ดีกว่าสำหรับการจัดการการแปลงแทนที่จะเป็นวิธีการ Convert.ChangeType เนื่องจากคุณจำเป็นต้องใช้อินเทอร์เฟซ IConvertible กับทุกคลาสของคุณ
Sauleil

3

คุณอาจจะใช้โครงสร้างเช่นระดับลักษณะ ด้วยวิธีนี้คุณจะมีคลาสตัวช่วยพารามิเตอร์ที่รู้วิธีแปลงสตริงเป็นค่าชนิดของตัวเอง จากนั้นผู้ทะเยอทะยานของคุณอาจมีลักษณะเช่นนี้:

get { return StringConverter<DataType>.FromString(base.Value); }

ตอนนี้ฉันต้องชี้ให้เห็นว่าประสบการณ์ของฉันกับประเภทพารามิเตอร์ถูก จำกัด ไว้ที่ C ++ และเทมเพลต แต่ฉันคิดว่ามีวิธีที่จะทำสิ่งเดียวกันโดยใช้ C # generics


รุ่นทั่วไปไม่มีอยู่ใน C #
Shimmy Weitzhandler

3

Nullable.GetUnderlyingTypeตรวจสอบคง - หากประเภทพื้นฐานเป็นโมฆะพารามิเตอร์เทมเพลตจะไม่Nullableและเราสามารถใช้ประเภทนั้นโดยตรง - หากประเภทพื้นฐานไม่เป็นโมฆะให้ใช้ประเภทพื้นฐานในการแปลง

ดูเหมือนว่าจะทำงานให้ฉัน:

public object Get( string _toparse, Type _t )
{
    // Test for Nullable<T> and return the base type instead:
    Type undertype = Nullable.GetUnderlyingType(_t);
    Type basetype = undertype == null ? _t : undertype;
    return Convert.ChangeType(_toparse, basetype);
}

public T Get<T>(string _key)
{
    return (T)Get(_key, typeof(T));
}

public void test()
{
    int x = Get<int>("14");
    int? nx = Get<Nullable<int>>("14");
}

1

ด้วยแรงบันดาลใจจากคำตอบของBobส่วนขยายเหล่านี้ยังสนับสนุนการแปลงค่า Null และการแปลงดั้งเดิมทั้งหมดกลับไปเป็นสี่

public static class ConversionExtensions
{
        public static object Convert(this object value, Type t)
        {
            Type underlyingType = Nullable.GetUnderlyingType(t);

            if (underlyingType != null && value == null)
            {
                return null;
            }
            Type basetype = underlyingType == null ? t : underlyingType;
            return System.Convert.ChangeType(value, basetype);
        }

        public static T Convert<T>(this object value)
        {
            return (T)value.Convert(typeof(T));
        }
}

ตัวอย่าง

            string stringValue = null;
            int? intResult = stringValue.Convert<int?>();

            int? intValue = null;
            var strResult = intValue.Convert<string>();

0
public class TypedProperty<T> : Property
{
    public T TypedValue
    {
        get { return (T)(object)base.Value; }
        set { base.Value = value.ToString();}
    }
}

ฉันใช้การแปลงผ่านวัตถุ มันง่ายกว่านิดหน่อย


ขอบคุณฉันต้องแปลง T ในอินเทอร์เฟซและการแปลงวัตถุ T อย่างง่าย ๆ ทำงานอย่างถูกต้องง่ายและรวดเร็วขอบคุณ
LXG

5
(int) (วัตถุ) "54"; คือ FATALITY !, นี่ไม่ใช่ VB!
Tomer W

0

ฉันใช้คำตอบ lobos และได้ผล แต่ฉันมีปัญหากับการแปลงเป็นสองเท่าเนื่องจากการตั้งค่าวัฒนธรรม ดังนั้นฉันเพิ่ม

return (T)Convert.ChangeType(base.Value, typeof(T), CultureInfo.InvariantCulture);

0

อีกรูปแบบหนึ่ง จับ Nullables เช่นเดียวกับสถานการณ์ที่สตริงเป็นโมฆะและ T คือไม่ nullable

public class TypedProperty<T> : Property where T : IConvertible
{
    public T TypedValue
    {
        get
        {
            if (base.Value == null) return default(T);
            var type = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T);
            return (T)Convert.ChangeType(base.Value, type);
        }
        set { base.Value = value.ToString(); }
    }
}

0

คุณสามารถทำได้ในหนึ่งบรรทัดดังต่อไปนี้:

YourClass obj = (YourClass)Convert.ChangeType(YourValue, typeof(YourClass));

Happy coding;)


เมื่อคุณแชร์รหัสเป็นคำตอบโปรดลองอธิบาย
Yunus Temurlenk
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.