ไม่สามารถแปลงประเภท 'Int' เป็น 'T' โดยปริยาย


92

โทรหาGet<int>(Stat);หรือGet<string>(Name);

แต่เมื่อรวบรวมฉันจะได้รับ:

ไม่สามารถแปลงประเภท 'int' เป็น 'T' โดยปริยาย

stringและสิ่งเดียวกัน

public T Get<T>(Stats type) where T : IConvertible
{
    if (typeof(T) == typeof(int))
    {
        int t = Convert.ToInt16(PlayerStats[type]);
        return t;
    }
    if (typeof(T) == typeof(string))
    {
        string t = PlayerStats[type].ToString();
        return t;
    }
}

6
คุณอาจคิดว่าถ้าบล็อกตรวจสอบว่า T เป็น int ดังนั้นภายในบล็อกคุณรู้ว่า T เป็น int และคุณควรจะสามารถแปลง int เป็น T โดยปริยาย แต่คอมไพเลอร์ไม่ได้ออกแบบมาเพื่อให้เป็นไปตามเหตุผลดังกล่าว แต่ก็รู้ ที่โดยทั่วไป T ไม่ได้มาจาก int ดังนั้นจึงไม่อนุญาตให้มีการแปลงโดยปริยาย (และหากคอมไพเลอร์รองรับคอมไพเลอร์ตัวตรวจสอบจะไม่ทำงานดังนั้นแอสเซมบลีที่คอมไพล์จะไม่สามารถตรวจสอบได้)
JGWeissman

คำตอบ:


132

เวลาที่คุณพบว่าตัวเองเปลี่ยนประเภทในทั่วไปคุณกำลังเกือบจะแน่นอนทำอะไรผิดพลาด Generics ควรจะทั่วไป ; พวกเขาควรจะทำงานเหมือนเป็นอิสระจากประเภท

หาก T สามารถเป็น int หรือสตริงได้เท่านั้นอย่าเขียนโค้ดของคุณด้วยวิธีนี้ตั้งแต่แรก เขียนสองวิธีวิธีหนึ่งที่ส่งกลับค่า int และหนึ่งที่ส่งคืนสตริง


1
รับ <Car> โดยที่รถใช้ IConvertible จะทำให้เกิดการแตกหัก เมื่อมีคนเห็นคุณมีวิธีการทั่วไปพวกเขาจะถือว่าพวกเขาสามารถส่งผ่านอะไรก็ได้ที่ใช้ IConvertible
Tjaart

11
ฉันเห็นด้วยกับคุณเพียงบางส่วน @Eric ฉันมีสถานการณ์ที่ฉันต้องแยกวิเคราะห์อาร์เรย์ที่เก็บไว้ในแท็ก XML ปัญหาคือข้อกำหนดที่เอกสาร XML ตามมา (COLLADA ในกรณีของฉัน) บอกว่าอาร์เรย์ดังกล่าวสามารถเป็นได้ ไม่เพียง แต่ float, int และ bool แต่ยังรวมถึงประเภทที่กำหนดเองอีกด้วยอย่างไรก็ตามในกรณีที่คุณได้รับ float [] (อาร์เรย์แท็กมีประเภทของข้อมูลที่เก็บไว้ในชื่อ: float_array stores ลอย) คุณต้องแยกวิเคราะห์สตริงเป็นอาร์เรย์ของ ลอยซึ่งต้องใช้ IFormatProvider บางตัว) เห็นได้ชัดว่าฉันไม่สามารถใช้ "T.Parse (... )" ได้ดังนั้นสำหรับบางกรณีเล็กน้อยฉันจำเป็นต้องใช้การสลับดังกล่าว
rbaleksandar

1
คำตอบนี้จะทำให้คุณไม่อยู่ในโพรงกระต่าย ฉันต้องการสร้างฟังก์ชันทั่วไปสำหรับint, int?, bool, bool?, stringและดูเหมือนจะเป็นไปไม่ได้
Jess

สิ่งนี้ทำให้การเปิดใช้งานประเภทที่แจกแจงทั่วไปสามารถใช้ได้จริง
David

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

143

คุณควรจะใช้Convert.ChangeType()แทนโค้ดที่กำหนดเองได้:

public T Get<T>(Stats type) where T : IConvertible
{
    return (T) Convert.ChangeType(PlayerStats[type], typeof(T));
}

22
แล้วreturn (T)(object)PlayerStats[type];
ล่ะ

@BrokenGlass ขอบคุณมันทำงานได้ดี
ASLIM

@maxp เพราะมันไม่ทำงานสำหรับวัตถุประสงค์ทั่วไปเช่นการหล่อประเภทที่ไม่มีการจัดการต้องใช้ 'Convert.ChangeType ()'
ASLIM

10
public T Get<T>(Stats type ) where T : IConvertible
{
    if (typeof(T) == typeof(int))
    {
        int t = Convert.ToInt16(PlayerStats[type]);
        return (T)t;
    }
    if (typeof(T) == typeof(string))
    {
        string t = PlayerStats[type].ToString();
        return (T)t;
    }
}

2
return (T) t;เนื่องจากไม่จำเป็นต้องตรวจสอบโมฆะ
BoltClock

ข้างต้นนี้จะไม่รวบรวมให้ฉัน T ต้องเป็นประเภทอ้างอิงสำหรับ "as" เพื่อคอมไพล์
Robert Schmidt

9

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

static void Main(string[] args)
{
    object number = "1";
    bool hasConverted;
    var convertedValue = DoConvert<int>(number, out hasConverted);

    Console.WriteLine(hasConverted);
    Console.WriteLine(convertedValue);
}

public static TConvertType DoConvert<TConvertType>(object convertValue, out bool hasConverted)
{
    hasConverted = false;
    var converted = default(TConvertType);
    try
    {
        converted = (TConvertType) 
            Convert.ChangeType(convertValue, typeof(TConvertType));
        hasConverted = true;
    }
    catch (InvalidCastException)
    {
    }
    catch (ArgumentNullException)
    {
    }
    catch (FormatException)
    {
    }
    catch (OverflowException)
    {
    }

    return converted;
}

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

8

ลองสิ่งนี้:

public T Get<T>(Stats type ) where T : IConvertible
{
    if (typeof(T) == typeof(int))
    {
        return (T)(object)Convert.ToInt16(PlayerStats[type]);

    }
    if (typeof(T) == typeof(string))
    {

        return (T)(object)PlayerStats[type];
    }
}

ขอบคุณที่ช่วยสิ่งนี้ความต้องการของฉันแตกต่างออกไป ฉันเขียนวิธีจำลองสำหรับวิธีการคงที่ที่มีอยู่เพื่อที่ฉันจะได้ทดสอบ โดยใช้osherove.com/blog/2012/7/8/…
Esen

8

ที่จริงแล้วคุณก็สามารถแปลงเป็นแล้วobjectT

T var = (T)(object)42;

ตัวอย่างสำหรับbool:

public class Program
{
    public static T Foo<T>()
    {
        if(typeof(T) == typeof(bool)) {
            return (T)(object)true;
        }

        return default(T);
    }

    public static void Main()
    {
        bool boolValue = Foo<bool>(); // == true
        string stringValue = Foo<string>(); // == null
    }
}

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


6

การพิจารณาตรรกะ @BrokenGlass ( Convert.ChangeType) ไม่รองรับประเภท GUID

public T Get<T>(Stats type) where T : IConvertible
{
    return (T) Convert.ChangeType(PlayerStats[type], typeof(T));
}

ข้อผิดพลาด : Cast จาก 'System.String' ถึง 'System.Guid' ไม่ถูกต้อง

ให้ใช้ตรรกะด้านล่างแทนTypeDescriptor.GetConverterโดยการเพิ่มSystem.ComponentModelเนมสเปซ

public T Get<T>(Stats type) where T : IConvertible
{
    (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFromInvariantString(PlayerStats[type])
}

อ่านนี้



0

คุณสามารถแคสต์ดังต่อไปนี้

public T Get<T>(Stats type) where T : IConvertible
{
  if (typeof(T) == typeof(int))
  {
    int t = Convert.ToInt16(PlayerStats[type]);
    return t as T;
  }
 if (typeof(T) == typeof(string))
 {
    string t = PlayerStats[type].ToString();
    return t as T;
 }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.