Generic TryParse


196

ฉันพยายามสร้างส่วนขยายทั่วไปที่ใช้ 'TryParse' เพื่อตรวจสอบว่าสตริงเป็นประเภทที่กำหนดหรือไม่:

public static bool Is<T>(this string input)
{
    T notUsed;
    return T.TryParse(input, out notUsed);
}

สิ่งนี้จะไม่รวบรวมเนื่องจากไม่สามารถแก้ไขสัญลักษณ์ 'TryParse'

ดังที่ฉันเข้าใจ 'TryParse' ไม่ได้เป็นส่วนหนึ่งของส่วนต่อประสานใด ๆ

เป็นไปได้ที่จะทำเลย?

ปรับปรุง:

ใช้คำตอบด้านล่างฉันมาด้วย:

public static bool Is<T>(this string input)
{
    try
    {
        TypeDescriptor.GetConverter(typeof(T)).ConvertFromString(input);
    }
    catch
    {
        return false;
    }

    return true;
}

มันใช้งานได้ค่อนข้างดี แต่ฉันคิดว่าการใช้ข้อยกเว้นในทางนั้นจะไม่เหมาะสมกับฉัน

Update2:

แก้ไขเพื่อส่งผ่านประเภทแทนที่จะใช้ generics:

public static bool Is(this string input, Type targetType)
{
    try
    {
        TypeDescriptor.GetConverter(targetType).ConvertFromString(input);
        return true;
    }
    catch
    {
        return false;
    }
}

1
ฉันคิดว่าในกรณีทั่วไปนี้คุณจะต้องจัดการกับ kludge ข้อยกเว้น คุณสามารถเพิ่มเคสเพื่อตรวจสอบสิ่งต่าง ๆ เช่น ints หรือ doubles แล้วใช้เมธอด TryParse เฉพาะ แต่คุณจะต้องถอยกลับเพื่อจับประเภทอื่น ๆ
ลุค

1
การใช้งานทั่วไปไม่จำเป็น เพียงผ่านในประเภทที่เป็นพารามิเตอร์ บูลสแตติกสาธารณะคือ (อินพุตสตริงนี้, พิมพ์ targetType) ด้วยวิธีการที่เรียกว่ามันดูดีกว่า: x.Is (typeof (int)) -VS- x.Is <int> ()
mikesigs

2
มีวิธี IsValid ในการแปลงให้คุณตรวจสอบว่าการแปลงจะมีปัญหาหรือไม่ ฉันใช้วิธีการด้านล่างและดูเหมือนว่าจะทำงานได้ดี protected Boolean TryParse<T>(Object value, out T result) { result = default(T); var convertor = TypeDescriptor.GetConverter(typeof(T)); if (convertor == null || !convertor.IsValid(value)) { return false; } result = (T)convertor.ConvertFrom(value); return true; }
CastroXXL

@CastroXXL ขอบคุณที่แสดงความสนใจในคำถามนี้ แต่วิธีการของคุณจะไม่ทำงานอย่างที่ฉันต้องการตรวจสอบว่าค่าสตริงเป็นประเภทที่แน่นอนมากกว่าวัตถุ แต่วิธีการของคุณจะเป็นประโยชน์สำหรับประเภทวัตถุ (แต่จะ ต้องห่อConvertFrom(value)วิธีในtry-catchบล็อกเพื่อจับข้อยกเว้น
เพียร์สไมเออร์

2
คุณควรตรวจสอบว่า (targetType == null) เพราะการใช้งานครั้งแรกในรหัสของคุณอาจโยน แต่ข้อยกเว้นนั้นจะถูกกลืนโดยการจับของคุณ
Nick Strupat

คำตอบ:


183

คุณควรใช้คลาสTypeDescriptor :

public static T Convert<T>(this string input)
{
    try
    {
        var converter = TypeDescriptor.GetConverter(typeof(T));
        if(converter != null)
        {
            // Cast ConvertFromString(string text) : object to (T)
            return (T)converter.ConvertFromString(input);
        }
        return default(T);
    }
    catch (NotSupportedException)
    {
        return default(T);
    }
}

3
ขออภัยที่จะฟื้นคืนชีพ แต่ GetConverter กลับเป็นโมฆะ? ฉันคิดว่าถ้ามันทำแล้วอาจจะมีข้อยกเว้นควรโยนแทนที่จะล้มเหลวอย่างเงียบ ๆ และส่งคืนสิ่งอื่น เมื่อฉันลองในชั้นเรียนของฉันเอง (ซึ่งฉันไม่ได้กำหนดตัวพิมพ์ดีด) ฉันได้รับตัวแปลงจาก GetConverter แต่จากนั้นตัวแปลง ConvertFromString ก็โยน NotSupportedException
user420667

3
@ user420667 ฉันเชื่อว่าคุณควรตรวจสอบผลลัพธ์ของ CanConvertFrom (typeof (string)) ก่อนที่จะพยายามแปลงจากสตริง TypeConverter อาจไม่รองรับการแปลงจากสตริง
Reuben Bond

3
คุณสามารถเพิ่มถ้า (typeof (T) .IsEnum) {return (T) Enum.Parse (typeof (T), อินพุต); } [เป็นทางลัดที่ค่อนข้างทั่วไปสำหรับ Enum ทุกประเภท] ก่อนรับตัวแปลง ฉันคิดว่ามันขึ้นอยู่กับว่าคุณจะทำ Enum บ่อยแค่ไหนเมื่อเทียบกับประเภทที่ซับซ้อนกว่า
Jesse Chisholm

10
ฉันไม่เข้าใจว่าทำไมสิ่งนี้จึงถูกทำเครื่องหมายว่าเป็นคำตอบและยกระดับขึ้นมากเมื่อไม่ได้ใช้สิ่งที่ร้องขอ: ลองแยกวิเคราะห์แบบทั่วไป วัตถุประสงค์หลักของวิธีการ TryParse ก็คือพวกเขาจะไม่ส่งข้อยกเว้นเมื่อพยายามทำการแยกวิเคราะห์และมีผลกระทบต่อประสิทธิภาพการทำงานต่ำกว่าเมื่อการแยกวิเคราะห์ล้มเหลวและวิธีนี้ไม่สามารถให้บริการได้
Florin Dumitrescu

2
ปัญหาหนึ่ง w / นี่คือถ้า T เป็น int และอินพุตมีขนาดใหญ่กว่า int.MaxValue ก็จะโยน System.Exception w / System.OverFlowException เป็นข้อยกเว้นภายใน ดังนั้นหากคุณคาดว่าจะมี OverflowException คุณจะไม่ได้รับยกเว้นว่าคุณได้ทำการสอบสวนข้อยกเว้นที่ส่งออกมา สาเหตุคือ ConvertFromString ส่ง OverflowException แล้วโยนไปที่ T เพื่อโยน System.Exception
เทรเวอร์

78

ฉันยังต้องการ TryParse ทั่วไปเมื่อเร็ว ๆ นี้ นี่คือสิ่งที่ฉันมาด้วย;

public static T? TryParse<T>(string value, TryParseHandler<T> handler) where T : struct
{
    if (String.IsNullOrEmpty(value))
        return null;
    T result;
    if (handler(value, out result))
        return result;
    Trace.TraceWarning("Invalid value '{0}'", value);
    return null;
}

public delegate bool TryParseHandler<T>(string value, out T result);

จากนั้นเป็นเพียงเรื่องของการโทรเท่านั้น:

var value = TryParse<int>("123", int.TryParse);
var value2 = TryParse<decimal>("123.123", decimal.TryParse);

3
เพิ่งเจอโพสต์นี้อีกหลายเดือนต่อมาและสังเกตว่าใช้อีกครั้งว่าวิธีการไม่สามารถอนุมานTจากตัวจัดการและเราจะต้องระบุอย่างชัดเจนTเมื่อเราเรียกมันว่า ฉันอยากรู้ว่าทำไมมันไม่อนุมานT?
Nick Strupat

26
ทำไมคุณต้องการใช้ฟังก์ชั่นนี้ หากคุณทราบว่าฟังก์ชันใดที่จะเรียกเพื่อวิเคราะห์ค่าทำไมไม่เรียกใช้โดยตรง มันรู้ชนิดอินพุตที่ถูกต้องแล้วและไม่จำเป็นต้องใช้ generics โซลูชันนี้จะไม่ทำงานสำหรับประเภทที่ไม่มี TryParseHandler
xxbbcc

2
@xxbbcc: ฉันต้องการใช้ฟังก์ชั่นนี้เพราะ TryParse ส่งคืนบูลีนที่ระบุว่าการแยกวิเคราะห์สำเร็จหรือไม่ มันส่งคืนค่าการแยกวิเคราะห์ของคุณผ่านพารามิเตอร์เอาท์พุท บางครั้งฉันก็แค่อยากจะทำอะไรเช่นนี้โดยไม่ต้องสร้างตัวแปรออกไปจับผลจากSomeMethod(TryParse<int>(DollarTextbox.Text, int.TryParse)) int.TryParseอย่างไรก็ตามฉันเห็นด้วยกับความเชื่อมั่นของ Nick เกี่ยวกับการมีฟังก์ชั่นอนุมานประเภท
Walter Stabosz

1
วิธีการที่มีประสิทธิภาพมาก แนะนำเป็นอย่างยิ่ง
Vladimir Kocjancic

3
ฉันอยากจะแนะนำค่าเริ่มต้นเป็นพารามิเตอร์ที่สาม ที่แก้ไขปัญหาที่ T ไม่สามารถอนุมานได้ นอกจากนี้ยังช่วยให้ผู้ใช้ตัดสินใจได้ว่าต้องการมูลค่าใดหากค่าสตริงไม่ถูกต้อง ตัวอย่างเช่น -1 อาจหมายถึงไม่ถูกต้อง สาธารณะคงที่ T TryParse <T> (ค่าสตริง, ตัวจัดการ TryParseHandler <T>, T defaultValue ค่า)
Rhyous

33

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

var attemptedValue = "asdfasdsd";
var type = typeof(int);
var converter = TypeDescriptor.GetConverter(type);
if (converter != null &&  converter.IsValid(attemptedValue))
    return converter.ConvertFromString(attemptedValue);
else
    return Activator.CreateInstance(type);

2
ฉันได้รับการแจ้งเตือน Resharper ที่converter != nullเป็นจริงเสมอดังนั้นจึงสามารถลบออกจากรหัสได้
ErikE

5
@ErikE ฉันไม่เชื่อถือคำเตือน ReSharper เหล่านั้นเสมอไป บ่อยครั้งที่พวกเขาไม่เห็นสิ่งที่เกิดขึ้นในขณะทำงาน
ProfK

1
@ProfK MSDN ไม่ได้บอกว่าสามารถคืนค่า null msdn.microsoft.com/en-us/library/ewtxwhzx.aspx ได้
danio

@danio ฉันเพิ่งแบ่งปันประสบการณ์ของฉันกับคำเตือน R # โดยทั่วไป แน่นอนฉันไม่ได้หมายความว่ามันผิดในกรณีนี้
ProfK

14

หากคุณตั้งค่าโดยใช้ TryParse คุณสามารถใช้การไตร่ตรองและทำดังนี้:

public static bool Is<T>(this string input)
{
    var type = typeof (T);
    var temp = default(T);
    var method = type.GetMethod(
        "TryParse",
        new[]
            {
                typeof (string),
                Type.GetType(string.Format("{0}&", type.FullName))
            });
    return (bool) method.Invoke(null, new object[] {input, temp});
}

มันเจ๋งมากและกำจัดข้อยกเว้นที่ฉันไม่ชอบอยู่ดี ยังค่อนข้างซับซ้อนแม้ว่า
Piers Myers

6
คำตอบที่ดี แต่คำตอบใด ๆ ที่เกี่ยวข้องกับการสะท้อน (โดยเฉพาะอย่างยิ่งในวิธีการยูทิลิตี้ที่สามารถเรียกได้ง่ายจากลูปด้านใน) จำเป็นต้องมีข้อจำกัดความรับผิดชอบเกี่ยวกับประสิทธิภาพ ดู: stackoverflow.com/questions/25458/how-costly-is-net-reflection
Patrick M

ถอนหายใจ ดังนั้นตัวเลือกคือ (1) ใช้ข้อยกเว้นสำหรับการควบคุมการไหลของรหัส (2) ใช้การสะท้อนกับต้นทุนความเร็ว ฉันเห็นด้วยกับ @PiersMyers - ตัวเลือกไม่เหมาะ สิ่งที่ดีพวกเขาทั้งสองทำงาน :)
Jesse Chisholm

ฉันคิดว่าคุณสามารถแทนที่ด้วยType.GetType(string.Format(...)) type.MakeByRefType()
Drew Noakes

3
วิธีการจะต้องสะท้อนให้เห็นเพียงครั้งเดียวต่อประเภทไม่ใช่หนึ่งครั้งต่อการโทร ถ้าคุณทำให้คลาสนี้เป็นตัวแปรสมาชิกแบบสแตติกคุณสามารถนำเอาท์พุตของการสะท้อนกลับมาใช้ใหม่ได้
Andrew Hill

7

สิ่งนี้ใช้ตัวสร้างแบบสแตติกสำหรับแต่ละประเภททั่วไปดังนั้นจึงต้องทำงานที่มีราคาแพงเพียงครั้งแรกที่คุณเรียกมันในประเภทที่กำหนด มันจัดการทุกประเภทใน namespace ระบบที่มีวิธี TryParse นอกจากนี้ยังทำงานกับเวอร์ชัน nullable ของแต่ละคน (นั่นคือ structs) ยกเว้นการแจกแจง

    public static bool TryParse<t>(this string Value, out t result)
    {
        return TryParser<t>.TryParse(Value.SafeTrim(), out result);
    }
    private delegate bool TryParseDelegate<t>(string value, out t result);
    private static class TryParser<T>
    {
        private static TryParseDelegate<T> parser;
        // Static constructor:
        static TryParser()
        {
            Type t = typeof(T);
            if (t.IsEnum)
                AssignClass<T>(GetEnumTryParse<T>());
            else if (t == typeof(bool) || t == typeof(bool?))
                AssignStruct<bool>(bool.TryParse);
            else if (t == typeof(byte) || t == typeof(byte?))
                AssignStruct<byte>(byte.TryParse);
            else if (t == typeof(short) || t == typeof(short?))
                AssignStruct<short>(short.TryParse);
            else if (t == typeof(char) || t == typeof(char?))
                AssignStruct<char>(char.TryParse);
            else if (t == typeof(int) || t == typeof(int?))
                AssignStruct<int>(int.TryParse);
            else if (t == typeof(long) || t == typeof(long?))
                AssignStruct<long>(long.TryParse);
            else if (t == typeof(sbyte) || t == typeof(sbyte?))
                AssignStruct<sbyte>(sbyte.TryParse);
            else if (t == typeof(ushort) || t == typeof(ushort?))
                AssignStruct<ushort>(ushort.TryParse);
            else if (t == typeof(uint) || t == typeof(uint?))
                AssignStruct<uint>(uint.TryParse);
            else if (t == typeof(ulong) || t == typeof(ulong?))
                AssignStruct<ulong>(ulong.TryParse);
            else if (t == typeof(decimal) || t == typeof(decimal?))
                AssignStruct<decimal>(decimal.TryParse);
            else if (t == typeof(float) || t == typeof(float?))
                AssignStruct<float>(float.TryParse);
            else if (t == typeof(double) || t == typeof(double?))
                AssignStruct<double>(double.TryParse);
            else if (t == typeof(DateTime) || t == typeof(DateTime?))
                AssignStruct<DateTime>(DateTime.TryParse);
            else if (t == typeof(TimeSpan) || t == typeof(TimeSpan?))
                AssignStruct<TimeSpan>(TimeSpan.TryParse);
            else if (t == typeof(Guid) || t == typeof(Guid?))
                AssignStruct<Guid>(Guid.TryParse);
            else if (t == typeof(Version))
                AssignClass<Version>(Version.TryParse);
        }
        private static void AssignStruct<t>(TryParseDelegate<t> del)
            where t: struct
        {
            TryParser<t>.parser = del;
            if (typeof(t).IsGenericType
                && typeof(t).GetGenericTypeDefinition() == typeof(Nullable<>))
            {
                return;
            }
            AssignClass<t?>(TryParseNullable<t>);
        }
        private static void AssignClass<t>(TryParseDelegate<t> del)
        {
            TryParser<t>.parser = del;
        }
        public static bool TryParse(string Value, out T Result)
        {
            if (parser == null)
            {
                Result = default(T);
                return false;
            }
            return parser(Value, out Result);
        }
    }

    private static bool TryParseEnum<t>(this string Value, out t result)
    {
        try
        {
            object temp = Enum.Parse(typeof(t), Value, true);
            if (temp is t)
            {
                result = (t)temp;
                return true;
            }
        }
        catch
        {
        }
        result = default(t);
        return false;
    }
    private static MethodInfo EnumTryParseMethod;
    private static TryParseDelegate<t> GetEnumTryParse<t>()
    {
        Type type = typeof(t);

        if (EnumTryParseMethod == null)
        {
            var methods = typeof(Enum).GetMethods(
                BindingFlags.Public | BindingFlags.Static);
            foreach (var method in methods)
                if (method.Name == "TryParse"
                    && method.IsGenericMethodDefinition
                    && method.GetParameters().Length == 2
                    && method.GetParameters()[0].ParameterType == typeof(string))
                {
                    EnumTryParseMethod = method;
                    break;
                }
        }
        var result = Delegate.CreateDelegate(
            typeof(TryParseDelegate<t>),
            EnumTryParseMethod.MakeGenericMethod(type), false)
            as TryParseDelegate<t>;
        if (result == null)
            return TryParseEnum<t>;
        else
            return result;
    }

    private static bool TryParseNullable<t>(string Value, out t? Result)
        where t: struct
    {
        t temp;
        if (TryParser<t>.TryParse(Value, out temp))
        {
            Result = temp;
            return true;
        }
        else
        {
            Result = null;
            return false;
        }
    }

6

แล้วเรื่องแบบนี้ล่ะ?

http://madskristensen.net/post/Universal-data-type-checker.aspx ( เอกสารเก่า )

/// <summary> 
/// Checks the specified value to see if it can be 
/// converted into the specified type. 
/// <remarks> 
/// The method supports all the primitive types of the CLR 
/// such as int, boolean, double, guid etc. as well as other 
/// simple types like Color and Unit and custom enum types. 
/// </remarks> 
/// </summary> 
/// <param name="value">The value to check.</param> 
/// <param name="type">The type that the value will be checked against.</param> 
/// <returns>True if the value can convert to the given type, otherwise false. </returns> 
public static bool CanConvert(string value, Type type) 
{ 
    if (string.IsNullOrEmpty(value) || type == null) return false;
    System.ComponentModel.TypeConverter conv = System.ComponentModel.TypeDescriptor.GetConverter(type);
    if (conv.CanConvertFrom(typeof(string)))
    { 
        try 
        {
            conv.ConvertFrom(value); 
            return true;
        } 
        catch 
        {
        } 
     } 
     return false;
  }

นี้สามารถแปลงเป็นวิธีการทั่วไปค่อนข้างง่าย

 public static bool Is<T>(this string value)
 {
    if (string.IsNullOrEmpty(value)) return false;
    var conv = System.ComponentModel.TypeDescriptor.GetConverter(typeof(T));

    if (conv.CanConvertFrom(typeof(string)))
    { 
        try 
        {
            conv.ConvertFrom(value); 
            return true;
        } 
        catch 
        {
        } 
     } 
     return false;
}

มันสำคัญหรือไม่ว่าคุณจะคืนค่าจริงจากบล็อกลองหรือกลับเท็จจากบล็อก catch หรือไม่ ฉันไม่คิด แต่ฉันยังคงคิดว่าการใช้ข้อยกเว้นด้วยวิธีนี้ทำให้ฉันรู้สึกผิด ...
Piers Myers

3
ไม่สำคัญว่าคุณจะกลับมาจากบล็อก catch หรือไม่ BTW ปกติแล้วมันจะไม่ดีที่จะมีประโยค catch ทั่วไป: catch { }. อย่างไรก็ตามในกรณีนี้ไม่มีทางเลือกเพราะ. NET BaseNumberConverterพ่นExceptionคลาสฐานในกรณีที่มีข้อผิดพลาดในการแปลง นี่เป็นเรื่องที่โชคร้ายมาก ในความเป็นจริงยังคงมีบางสถานที่ที่ถูกโยนฐานประเภทนี้ถูกโยน หวังว่า Microsoft จะแก้ไขปัญหาเหล่านี้ในกรอบงานรุ่นต่อไป
Steven

ขอบคุณสตีเวนไม่สามารถพูดได้ดีกว่านี้
Bob

ไม่มีการใช้ประโยชน์จากผลลัพธ์ของการแปลง: รหัสซ้ำซ้อน
BillW

4

คุณไม่สามารถทำได้กับประเภททั่วไป

สิ่งที่คุณสามารถทำได้คือการสร้างอินเทอร์เฟซ ITryParsable และใช้สำหรับประเภทกำหนดเองที่ใช้อินเทอร์เฟซนี้

ผมคิดว่าที่คุณตั้งใจจะใช้นี้กับประเภทพื้นฐานเช่นและint DateTimeคุณไม่สามารถเปลี่ยนประเภทเหล่านี้เพื่อนำอินเตอร์เฟสใหม่มาใช้


1
ฉันสงสัยว่าจะทำงานได้โดยใช้คำหลักแบบไดนามิกใน. net 4 หรือไม่
Pierre-Alain Vigeant

@Pierre: สิ่งนี้จะไม่ทำงานตามค่าเริ่มต้นใน C # ด้วยdynamicคำหลักเนื่องจากมันจะไม่ทำงานกับการพิมพ์แบบคงที่ คุณสามารถสร้างวัตถุไดนามิกของคุณเองที่สามารถจัดการสิ่งนี้ได้ แต่มันไม่ได้เป็นค่าเริ่มต้น
Steven

4

แรงบันดาลใจจากโซลูชันที่โพสต์ที่นี่โดย Charlie Brown ฉันสร้าง TryParse ทั่วไปโดยใช้การสะท้อนที่เลือกผลลัพธ์ที่แยกวิเคราะห์ค่า:

/// <summary>
/// Tries to convert the specified string representation of a logical value to
/// its type T equivalent. A return value indicates whether the conversion
/// succeeded or failed.
/// </summary>
/// <typeparam name="T">The type to try and convert to.</typeparam>
/// <param name="value">A string containing the value to try and convert.</param>
/// <param name="result">If the conversion was successful, the converted value of type T.</param>
/// <returns>If value was converted successfully, true; otherwise false.</returns>
public static bool TryParse<T>(string value, out T result) where T : struct {
    var tryParseMethod = typeof(T).GetMethod("TryParse", BindingFlags.Static | BindingFlags.Public, null, new [] { typeof(string), typeof(T).MakeByRefType() }, null);
    var parameters = new object[] { value, null };

    var retVal = (bool)tryParseMethod.Invoke(null, parameters);

    result = (T)parameters[1];
    return retVal;
}

/// <summary>
/// Tries to convert the specified string representation of a logical value to
/// its type T equivalent. A return value indicates whether the conversion
/// succeeded or failed.
/// </summary>
/// <typeparam name="T">The type to try and convert to.</typeparam>
/// <param name="value">A string containing the value to try and convert.</param>
/// <returns>If value was converted successfully, true; otherwise false.</returns>
public static bool TryParse<T>(string value) where T : struct {
    T throwaway;
    var retVal = TryParse(value, out throwaway);
    return retVal;
}

มันสามารถเรียกได้ว่า:

string input = "123";
decimal myDecimal;

bool myIntSuccess = TryParse<int>(input);
bool myDecimalSuccess = TryParse<decimal>(input, out myDecimal);

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

/// <summary>
/// Provides some extra parsing functionality for value types.
/// </summary>
/// <typeparam name="T">The value type T to operate on.</typeparam>
public static class TryParseHelper<T> where T : struct {
    private delegate bool TryParseFunc(string str, out T result);

    private static TryParseFunc tryParseFuncCached;

    private static TryParseFunc tryParseCached {
        get {
            return tryParseFuncCached ?? (tryParseFuncCached = Delegate.CreateDelegate(typeof(TryParseFunc), typeof(T), "TryParse") as TryParseFunc);
        }
    }

    /// <summary>
    /// Tries to convert the specified string representation of a logical value to
    /// its type T equivalent. A return value indicates whether the conversion
    /// succeeded or failed.
    /// </summary>
    /// <param name="value">A string containing the value to try and convert.</param>
    /// <param name="result">If the conversion was successful, the converted value of type T.</param>
    /// <returns>If value was converted successfully, true; otherwise false.</returns>
    public static bool TryParse(string value, out T result) {
        return tryParseCached(value, out result);
    }

    /// <summary>
    /// Tries to convert the specified string representation of a logical value to
    /// its type T equivalent. A return value indicates whether the conversion
    /// succeeded or failed.
    /// </summary>
    /// <param name="value">A string containing the value to try and convert.</param>
    /// <returns>If value was converted successfully, true; otherwise false.</returns>
    public static bool TryParse(string value) {
        T throwaway;
        return TryParse(value, out throwaway);
    }
}

เรียกว่าเป็นแบบนี้:

string input = "987";
decimal myDecimal;

bool myIntSuccess = TryParseHelper<int>.TryParse(input);
bool myDecimalSuccess = TryParseHelper<decimal>.TryParse(input, out myDecimal);

3

ค่อนข้างช้าไปงานปาร์ตี้ แต่นี่คือสิ่งที่ฉันมาด้วย ไม่มีข้อยกเว้นสะท้อนครั้งเดียว (ต่อประเภท)

public static class Extensions {
    public static T? ParseAs<T>(this string str) where T : struct {
        T val;
        return GenericHelper<T>.TryParse(str, out val) ? val : default(T?);
    }
    public static T ParseAs<T>(this string str, T defaultVal) {
        T val;
        return GenericHelper<T>.TryParse(str, out val) ? val : defaultVal;
    }

    private static class GenericHelper<T> {
        public delegate bool TryParseFunc(string str, out T result);

        private static TryParseFunc tryParse;
        public static TryParseFunc TryParse {
            get {
                if (tryParse == null)
                    tryParse = Delegate.CreateDelegate(
                        typeof(TryParseFunc), typeof(T), "TryParse") as TryParseFunc;
                return tryParse;
            }
        }
    }
}

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

"5643".ParseAs<int>()

3

นี่คือตัวเลือกอื่น

ผมเขียนระดับที่ทำให้มันง่ายที่จะลงทะเบียนจำนวนใด ๆTryParseไส มันช่วยให้ฉันทำสิ่งนี้:

var tp = new TryParser();

tp.Register<int>(int.TryParse);
tp.Register<decimal>(decimal.TryParse);
tp.Register<double>(double.TryParse);

int x;
if (tp.TryParse("42", out x))
{
    Console.WriteLine(x);
};

ฉัน42พิมพ์ไปที่คอนโซล

ชั้นเรียนคือ:

public class TryParser
{
    public delegate bool TryParseDelegate<T>(string s, out T result);

    private Dictionary<Type, Delegate> _tryParsers = new Dictionary<Type, Delegate>();

    public void Register<T>(TryParseDelegate<T> d)
    {
        _tryParsers[typeof(T)] = d;
    }

    public bool Deregister<T>()
    {
        return _tryParsers.Remove(typeof(T));
    }

    public bool TryParse<T>(string s, out T result)
    {
        if (!_tryParsers.ContainsKey(typeof(T)))
        {
            throw new ArgumentException("Does not contain parser for " + typeof(T).FullName + ".");
        }
        var d = (TryParseDelegate<T>)_tryParsers[typeof(T)];
        return d(s, out result);
    }
}

ฉันชอบสิ่งนี้ แต่คุณจะทำอย่างไรโดยไม่ใช้ยาชื่อสามัญ ใช้กรณีเป็นภาพสะท้อนของหลักสูตร
Sinaesthetic

ฉันได้เพิ่มวิธีโอเวอร์โหลดที่จะทำการแฮ็กแฮ็ก หากมีวิธีที่สง่างามกว่าในการจัดการกับสิ่งต่าง ๆ ฉันขอให้ทุกสายตา lol gist.github.com/dasjestyr/90d8ef4dea179a6e08ddd85e0dacbc94
Sinaesthetic

2

เมื่อฉันต้องการทำสิ่งที่แน่นอนเกือบทั้งหมดนี้ฉันต้องใช้มันอย่างหนักเพื่อให้ได้ภาพสะท้อน ได้รับTสะท้อนให้เห็นถึงtypeof(T)และมองหาTryParseหรือParseวิธีการเรียกมันหากคุณได้พบมัน


นี่คือสิ่งที่ฉันอยากจะแนะนำ
Steven Evers

2

นี่คือความพยายามของฉัน ฉันทำมันเป็น "ออกกำลังกาย" ฉันพยายามทำให้มันคล้ายกับที่ใช้เป็น " Convert.ToX () " ที่มีอยู่แล้วฯลฯ แต่วิธีนี้เป็นวิธีการขยาย:

    public static bool TryParse<T>(this String str, out T parsedValue)
    {
        try
        {
            parsedValue = (T)Convert.ChangeType(str, typeof(T));
            return true;
        }

        catch { parsedValue = default(T); return false; }
    }

ข้อเสียเปรียบหลักของการเปรียบเทียบนี้TypeConverter.ConvertFrom()คือคลาสต้นทางต้องจัดให้มีการแปลงประเภทซึ่งโดยทั่วไปหมายความว่าคุณไม่สามารถรองรับการแปลงเป็นประเภทที่กำหนดเองได้
เอียนโกลด์บาย

1

อย่างที่คุณบอกTryParseว่าไม่ได้เป็นส่วนหนึ่งของอินเทอร์เฟซ มันไม่ได้เป็นสมาชิกของคลาสพื้นฐานใด ๆ เนื่องจากเป็นจริงstaticและstaticฟังก์ชันไม่สามารถเป็นvirtualได้ ดังนั้นคอมไพเลอร์จึงไม่มีทางมั่นใจได้ว่าTมีสมาชิกชื่อจริงTryParseจริงดังนั้นมันจึงใช้งานไม่ได้

ตามที่ @ Mark กล่าวว่าคุณสามารถสร้างอินเทอร์เฟซของคุณเองและใช้ประเภทที่กำหนดเองได้ แต่คุณโชคไม่ดีสำหรับประเภทในตัว


1
public static class Primitive
{
    public static DateTime? TryParseExact(string text, string format, IFormatProvider formatProvider = null, DateTimeStyles? style = null)
    {
        DateTime result;
        if (DateTime.TryParseExact(text, format, formatProvider, style ?? DateTimeStyles.None, out result))
            return result;
        return null;
    }

    public static TResult? TryParse<TResult>(string text) where TResult : struct
    {
        TResult result;
        if (Delegates<TResult>.TryParse(text, out result))
            return result;
        return null;
    }

    public static bool TryParse<TResult>(string text, out TResult result) => Delegates<TResult>.TryParse(text, out result);

    public static class Delegates<TResult>
    {
        private delegate bool TryParseDelegate(string text, out TResult result);

        private static readonly TryParseDelegate _parser = (TryParseDelegate)Delegate.CreateDelegate(typeof(TryParseDelegate), typeof(TResult), "TryParse");

        public static bool TryParse(string text, out TResult result) => _parser(text, out result);
    }
}

0

นี่เป็นคำถามของ 'ข้อ จำกัด ทั่วไป' เนื่องจากคุณไม่มีอินเทอร์เฟซที่เฉพาะเจาะจงดังนั้นคุณจะติดอยู่เว้นแต่ว่าคุณทำตามคำแนะนำของคำตอบก่อนหน้า

สำหรับเอกสารเกี่ยวกับเรื่องนี้ให้ตรวจสอบลิงค์ต่อไปนี้:

http://msdn.microsoft.com/en-us/library/ms379564(VS.80).aspx

มันแสดงให้คุณเห็นถึงวิธีการใช้ข้อ จำกัด เหล่านี้และควรให้ข้อมูลเพิ่มเติมแก่คุณ


0

ยืมจากhttp://blogs.msdn.com/b/davidebb/archive/2009/10/23/using-c-dynamic-to-call-static-members.aspx

เมื่อติดตามการอ้างอิงนี้: วิธีการเรียกใช้วิธีการคงที่ใน C # 4.0 ด้วยประเภทแบบไดนามิก?

using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Reflection;

namespace Utils
{
   public class StaticMembersDynamicWrapper : DynamicObject
   {
      private Type _type;

      public StaticMembersDynamicWrapper(Type type) { _type = type; }

      // Handle static methods
      public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
      {
         var methods = _type
            .GetMethods(BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public)
            .Where(methodInfo => methodInfo.Name == binder.Name);

         var method = methods.FirstOrDefault();
         if (method != null)
         {
            result = method.Invoke(null, args);
            return true;
         }

         result = null;
         return false;
      }
   }

   public static class StaticMembersDynamicWrapperExtensions
   {
      static Dictionary<Type, DynamicObject> cache =
         new Dictionary<Type, DynamicObject>
         {
            {typeof(double), new StaticMembersDynamicWrapper(typeof(double))},
            {typeof(float), new StaticMembersDynamicWrapper(typeof(float))},
            {typeof(uint), new StaticMembersDynamicWrapper(typeof(uint))},
            {typeof(int), new StaticMembersDynamicWrapper(typeof(int))},
            {typeof(sbyte), new StaticMembersDynamicWrapper(typeof(sbyte))}
         };

      /// <summary>
      /// Allows access to static fields, properties, and methods, resolved at run-time.
      /// </summary>
      public static dynamic StaticMembers(this Type type)
      {
         DynamicObject retVal;
         if (!cache.TryGetValue(type, out retVal))
            return new StaticMembersDynamicWrapper(type);

         return retVal;
      }
   }
}

และใช้มันดังต่อไปนี้:

  public static T? ParseNumeric<T>(this string str, bool throws = true)
     where T : struct
  {
     var statics = typeof(T).StaticMembers();

     if (throws) return statics.Parse(str);

     T retval;
     if (!statics.TryParse(str, out retval)) return null;

     return retval;
  }

0

ฉันจัดการเพื่อให้ได้สิ่งที่ใช้งานได้เช่นนี้

    var result = "44".TryParse<int>();

    Console.WriteLine( "type={0}, value={1}, valid={2}",        
    result.Value.GetType(), result.Value, result.IsValid );

นี่คือรหัสของฉัน

 public static class TryParseGeneric
    {
        //extend int
        public static dynamic TryParse<T>( this string input )
        {    
            dynamic runner = new StaticMembersDynamicWrapper( typeof( T ) );

            T value;
            bool isValid = runner.TryParse( input, out value );
            return new { IsValid = isValid, Value = value };
        }
    }


    public class StaticMembersDynamicWrapper : DynamicObject
    {
        private readonly Type _type;
        public StaticMembersDynamicWrapper( Type type ) { _type = type; }

        // Handle static properties
        public override bool TryGetMember( GetMemberBinder binder, out object result )
        {
            PropertyInfo prop = _type.GetProperty( binder.Name, BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public );
            if ( prop == null )
            {
                result = null;
                return false;
            }

            result = prop.GetValue( null, null );
            return true;
        }

        // Handle static methods
        public override bool TryInvokeMember( InvokeMemberBinder binder, object [] args, out object result )
        {
            var methods = _type
            .GetMethods( BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public ).Where( methodInfo => methodInfo.Name == binder.Name );

            var method = methods.FirstOrDefault();

            if ( method == null )
            {
                result = null;

                return false;
            }

            result = method.Invoke( null, args );

            return true;
        }
    }

StaticMembersDynamicWrapper ถูกดัดแปลงจากบทความของ David Ebbo (มันกำลังขว้าง AmbiguousMatchException)



0

ด้วยTypeDescriptorการใช้คลาสในTryParseลักษณะที่เกี่ยวข้อง:

public static bool TryParse<T>(this string input, out T parsedValue)
{
    parsedValue = default(T);
    try
    {
        var converter = TypeDescriptor.GetConverter(typeof(T));
        parsedValue = (T)converter.ConvertFromString(input);
        return true;
    }
    catch (NotSupportedException)
    {
        return false;
    }
}

แม้ว่ารหัสนี้อาจแก้ปัญหาได้รวมถึงคำอธิบายว่าทำไมและวิธีแก้ปัญหานี้จะช่วยปรับปรุงคุณภาพการโพสต์ของคุณได้อย่างไรและอาจส่งผลให้คะแนนมากขึ้น จำไว้ว่าคุณกำลังตอบคำถามสำหรับผู้อ่านในอนาคตไม่ใช่เพียงแค่คนที่ถามตอนนี้ โปรดแก้ไขคำตอบของคุณเพื่อเพิ่มคำอธิบายและระบุข้อ จำกัด และสมมติฐานที่ใช้
เสียงบี๊บสองครั้ง

0

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

ฉันแคชเมธอดในพจนานุกรมขณะที่แต่ละวิธีพบเพื่อลดวิธีดึงโหลด

เป็นไปได้ที่จะทดสอบว่าวัตถุสามารถแปลงเป็นประเภทเป้าหมายได้โดยตรงหรือไม่ซึ่งจะช่วยลดส่วนการแปลงสตริง แต่ตอนนี้ฉันจะออกไปก่อน

    /// <summary>
    /// Used to store TryParse converter methods
    /// </summary>
    private static readonly Dictionary<Type, MethodInfo> TypeConverters = new Dictionary<Type, MethodInfo>();

    /// <summary>
    /// Attempt to parse the input object to the output type
    /// </summary>
    /// <typeparam name="T">output type</typeparam>
    /// <param name="obj">input object</param>
    /// <param name="result">output result on success, default(T) on failure</param>
    /// <returns>Success</returns>
    public static bool TryParse<T>([CanBeNull] object obj, out T result)
    {
        result = default(T);

        try
        {
            switch (obj)
            {
                // don't waste time on null objects
                case null: return false;

                // if the object is already of type T, just return the value
                case T val:
                    result = val;
                    return true;
            }

            // convert the object into type T via string conversion
            var input = ((obj as string) ?? obj.ToString()).Trim();
            if (string.IsNullOrEmpty(input)) return false;

            var type = typeof (T);
            Debug.WriteLine($"Info: {nameof(TryParse)}<{type.Name}>({obj.GetType().Name}=\"{input}\")");

            if (! TypeConverters.TryGetValue(type, out var method))
            {
                // get the TryParse method for this type
                method = type.GetMethod("TryParse",
                    new[]
                    {
                        typeof (string),
                        Type.GetType($"{type.FullName}&")
                    });

                if (method is null)
                    Debug.WriteLine($"FAILED: Cannot get method for {type.Name}.TryParse()");

                // store it so we don't have to do this again
                TypeConverters.Add(type, method);
            }

            // have to keep a reference to parameters if you want to get the returned ref value
            var parameters = new object[] {input, null};
            if ((bool?) method?.Invoke(null, parameters) == true)
            {
                result = (T) parameters[1];
                return true;
            }                
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex);
        }

        return false;
    }

ฉันต้องเพิ่มฟังก์ชั่นอื่นเพื่อรองรับการแจกแจง ดูเหมือนว่าการแยกวิเคราะห์ Enum ต้องใช้แอตทริบิวต์ "where T: struct" และฉันต้องการให้มันทำงานกับอะไรที่เปลี่ยนแปลงได้ (น่าจะเพิ่มแอตทริบิวต์ที่เปลี่ยนแปลงได้ให้กับประเภท) อย่างไรก็ตามคำแนะนำต่อไปนี้บางข้อดูเรียบง่ายกว่า
B Duffy

0

ฉันรวบรวมความคิดเห็นไว้ที่นี่และจบลงด้วยวิธีแก้ปัญหาสั้น ๆ

นี่เป็นวิธีส่วนขยายของสตริง

enter code here

ฉันทำมันด้วยการพิมพ์แบบเท้าเดียวกับเมธอด TryParse บนชนิดตัวเลข

    /// <summary>
    /// string.TryParse()
    /// 
    /// This generic extension method will take a string
    ///     make sure it is not null or empty
    ///     make sure it represents some type of number e.g. "123" not "abc"
    ///     It then calls the appropriate converter for the type of T
    /// </summary>
    /// <typeparam name="T">The type of the desired retrunValue e.g. int, float, byte, decimal...</typeparam>
    /// <param name="targetText">The text to be converted</param>
    /// <param name="returnValue">a populated value of the type T or the default(T) value which is likely to be 0</param>
    /// <returns>true if the string was successfully parsed and converted otherwise false</returns>
    /// <example>
    /// float testValue = 0;
    ///  if ( "1234".TryParse<float>( out testValue ) )
    ///  {
    ///      doSomethingGood();
    ///  }
    ///  else
    ///  {
    ///      handleTheBadness();
    ///  }
    /// </example>
    public static bool TryParse<T>(this string targetText, out T returnValue )
    {
        bool returnStatus = false;

        returnValue = default(T);

        //
        // make sure the string is not null or empty and likely a number...
        // call whatever you like here or just leave it out - I would
        // at least make sure the string was not null or empty  
        //
        if ( ValidatedInputAnyWayYouLike(targetText) )
        {

            //
            // try to catch anything that blows up in the conversion process...
            //
            try
            {
                var type = typeof(T);
                var converter = TypeDescriptor.GetConverter(type);

                if (converter != null && converter.IsValid(targetText))
                {
                    returnValue = (T)converter.ConvertFromString(targetText);
                    returnStatus = true;
                }

            }
            catch
            {
                // just swallow the exception and return the default values for failure
            }

        }

        return (returnStatus);

    }

'''


float testValue = 0; if ("1234" .TryParse <float> (ออก testValue)) {doSomethingGood (); } else {handleTheBadness (); }
JD Hicks

0

T.TryParse ... ทำไมล่ะ

ฉันไม่เห็นประโยชน์ในการมีTryParseฟังก์ชั่นทั่วไปดังกล่าว มีกลยุทธ์ที่แตกต่างกันมากเกินไปสำหรับการแยกวิเคราะห์และแปลงข้อมูลระหว่างประเภทต่าง ๆ กับพฤติกรรมที่ขัดแย้งกันที่อาจเกิดขึ้นได้ ฟังก์ชั่นนี้จะรู้ได้อย่างไรว่ากลยุทธ์ใดที่ควรเลือกแบบไม่มีบริบท?

  • คลาสที่มีฟังก์ชั่น TryParse เฉพาะสามารถเรียกได้
  • คลาสที่มีฟังก์ชั่นการวิเคราะห์คำแยกเฉพาะสามารถห่อด้วยผลลัพธ์การทดลองและผลบูลได้
  • คลาสที่มีโอเปอเรเตอร์โอเวอร์โหลดคุณจะให้พวกมันจัดการการแจงได้อย่างไร?
  • อธิบายชนิดที่มีในตัวโดยใช้ Convert.ChangeTypeประเภทอธิบายในตัวโดยใช้API นี้สามารถปรับแต่งได้ที่รันไทม์ ฟังก์ชั่นของคุณต้องการพฤติกรรมเริ่มต้นหรืออนุญาตสำหรับการปรับแต่ง?
  • คุณควรให้กรอบการแมปใด ๆ พยายามแยกวิเคราะห์สำหรับคุณหรือไม่
  • คุณจะจัดการกับความขัดแย้งในด้านบนอย่างไร

-2

เวอร์ชันสำหรับรับลูกหลานจาก XDocument

public static T Get<T>(XDocument xml, string descendant, T @default)
{
    try
    {
        var converter = TypeDescriptor.GetConverter(typeof (T));
        if (converter != null)
        {
            return (T) converter.ConvertFromString(xml.Descendants(descendant).Single().Value);
        }
        return @default;
    }
    catch
    {
        return @default;
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.