ไม่สามารถแปลงค่าประเภท 'T'


146

เป็นไปได้ว่าคำถามนี้จะเป็นเรื่องเริ่มต้น แต่ Google ก็ไม่ได้ให้คำตอบที่น่าประหลาดใจ

ฉันมีวิธีประดิษฐ์นี้ค่อนข้าง

T HowToCast<T>(T t)
{
    if (typeof(T) == typeof(string))
    {
        T newT1 = "some text";
        T newT2 = (string)t;
    }

    return t;
}

มาจากพื้นหลัง C ++ ฉันคาดว่าสิ่งนี้จะทำงาน อย่างไรก็ตามมันไม่สามารถคอมไพล์ด้วย "ไม่สามารถแปลงชนิด 'T' เป็นสตริง" และ "ไม่สามารถแปลงชนิด 'T' เป็นสตริง" สำหรับการมอบหมายทั้งสองอย่างข้างต้น

ฉันกำลังทำอะไรบางอย่างผิดพลาดทางแนวคิดหรือเพิ่งมีไวยากรณ์ผิด โปรดช่วยฉันเรียงลำดับออก

ขอบคุณ!


20
IMO หากคุณกำลังตรวจสอบประเภทในรหัส generics ของคุณ generics อาจไม่ใช่วิธีการแก้ไขปัญหาของคุณที่ถูกต้อง
Austin Salonen

นิพจน์typeof(T) == typeof(string)ได้รับการแก้ไขที่รันไทม์ไม่ใช่เวลาคอมไพล์ ดังนั้นบรรทัดต่อไปนี้ในบล็อกจึงไม่ถูกต้อง
Steve Guidi

8
(T) Convert.ChangeType (newT1, typeof (T))
vsapiha

2
@vsapiha ใช้ได้เฉพาะกับวัตถุที่ใช้ IConvertible ความหวานถ้ามันทำ
ouflak

คำตอบ:


285

แม้ว่ามันจะเป็นเรื่องภายในของifบล็อกคอมไพเลอร์ไม่ทราบว่าเป็นT ดังนั้นมันไม่อนุญาตให้คุณร่าย (ด้วยเหตุผลเดียวกับที่คุณไม่สามารถส่งไปได้)string
DateTimestring

คุณต้องร่ายไปหาobject(ซึ่งใครก็ตามTสามารถร่ายไปหาได้) และจากที่นั่นไปยังstring(เนื่องจากobjectสามารถร่ายได้string)
ตัวอย่างเช่น:

T newT1 = (T)(object)"some text";
string newT2 = (string)(object)t;

2
มันใช้งานได้! ฉันคาดเดาอย่างที่สองเช่นนั้นควรเป็น T newT2 = (T) (วัตถุ) t; แม้ว่านั่นจะไม่ใช่ op
อเล็กซ์

2
นอกจากนี้: เทมเพลต C ++ ส่วนใหญ่จะถูกตัดและวางในเวลารวบรวมด้วยค่าที่ถูกต้องที่ถูกแทนที่ ใน C # เทมเพลตทั่วไปที่เกิดขึ้นจริง (ไม่ใช่ "การสร้างอินสแตนซ์" ของมัน) จะมีอยู่หลังจากการรวบรวมและดังนั้นจึงต้อง (ให้อภัย the pun) ในรูปแบบทั่วไปทั่วขอบเขตประเภทที่ระบุ

(สตริง) (วัตถุ) เสื้อ; ไม่ได้ทำอะไรที่นี่แม้ว่าเช่นกันอาจทิ้งที่ (สตริง) (วัตถุ) นั่นคือ
Doggett

6
ทำไมไม่ลองส่งโดยใช้ "เป็นสตริง"? ตัวอย่างเช่นการคอมไพล์นี้ดี (ฉันเพียงแค่รวบรวมโดยไม่มีข้อผิดพลาด) เมื่อผู้ใช้กำหนดค่าเป็นประเภทT:var isBlank = (userDefinedValue is string) && String.IsNullOrWhiteSpace(userDefinedValue as string);
Triynko

1
สิ่งนี้ให้ความรู้สึกผิดพลาดโดยผู้ออกแบบคอมไพเลอร์ ถ้า T ทั้งหมดสามารถถูกทิ้งลงในวัตถุอย่างชัดเจนและวัตถุทั้งหมดสามารถถูกส่งไปยังสายอักขระอย่างชัดเจนแล้วก็ควรมีกฎสกรรมกริยาที่ T สามารถแปลงเป็นสตริงได้อย่างชัดเจน หากคุณทำการโยนไม่ถูกต้องจะเกิดข้อผิดพลาดขณะทำงาน
P.Brian.Mackey

10

ทั้งสองบรรทัดมีปัญหาเดียวกัน

T newT1 = "some text";
T newT2 = (string)t;

คอมไพเลอร์ไม่ทราบว่า T เป็นสตริงดังนั้นจึงไม่มีวิธีรู้วิธีกำหนดสิ่งนั้น แต่เมื่อคุณตรวจสอบคุณก็สามารถบังคับได้ด้วย

T newT1 = "some text" as T;
T newT2 = t; 

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

where T : class

2
ไม่ถูกต้อง. สิ่งนี้จะไม่คอมไพล์ ดูคำตอบของฉัน
SLaks

2
คอมไพล์ได้ดี (ด้วยที่ที่คือเพิ่มว่าไม่กี่วินาทีหลังจากที่ฉันโพสต์อาจพลาด) โอ๊ะโอลืมเปลี่ยนนักแสดง
Doggett

2

ฉันรู้ว่ารหัสที่คล้ายกันที่ OP โพสต์ในคำถามนี้จาก parsers ทั่วไป จากมุมมองประสิทธิภาพคุณควรใช้Unsafe.As<TFrom, TResult>(ref TFrom source)ซึ่งสามารถพบได้ในแพ็คเกจSystem.Runtime.CompilerServices.Unsafe NuGet มันหลีกเลี่ยงการชกมวยสำหรับประเภทค่าในสถานการณ์เหล่านี้ ฉันยังคิดว่าUnsafe.Asผลลัพธ์ที่ได้ในโค้ดของเครื่องที่ผลิตโดย JIT น้อยกว่าการแคสต์สองครั้ง (โดยใช้(TResult) (object) actualString) แต่ฉันยังไม่ได้ตรวจสอบ

public TResult ParseSomething<TResult>(ParseContext context)
{
    if (typeof(TResult) == typeof(string))
    {
        var token = context.ParseNextToken();
        string parsedString = token.ParseToDotnetString();
        return Unsafe.As<string, TResult>(ref parsedString);
    }
    else if (typeof(TResult) == typeof(int))
    {
        var token = context.ParseNextToken();
        int parsedInt32 = token.ParseToDotnetInt32();
        // This will not box which might be critical to performance
        return Unsafe.As<int, TResult>(ref parsedInt32); 
    }
    // other cases omitted for brevity's sake
}

Unsafe.As จะถูกแทนที่ด้วย JIT ด้วยคำแนะนำรหัสเครื่องจักรที่มีประสิทธิภาพอย่างที่คุณเห็นใน repo CoreFX อย่างเป็นทางการ:

รหัสที่มาของ Unsafe.As


1

หากคุณกำลังตรวจสอบประเภทที่ชัดเจนทำไมคุณถึงประกาศตัวแปรเหล่านั้นเป็นT?

T HowToCast<T>(T t)
{
    if (typeof(T) == typeof(string))
    {
        var newT1 = "some text";
        var newT2 = t;  //this builds but I'm not sure what it does under the hood.
        var newT3 = t.ToString();  //for sure the string you want.
    }

    return t;
}

6
Tบรรทัดที่สองสร้างตัวแปรประเภท
SLaks

คุณถามว่าทำไมตรวจสอบประเภท? สมมติว่าคุณมีประเภทเขตข้อมูลพื้นฐานที่เก็บobjectค่ากับประเภทที่ได้รับมาซึ่งเก็บstringค่า สมมติว่าข้อมูลเหล่านี้ยังมี "DefaultIfNotProvided" ค่าเพื่อให้คุณต้องตรวจสอบว่าค่าที่ผู้ใช้ให้ไว้ (ซึ่งอาจจะเป็นวัตถุหรือสตริงหรือแม้กระทั่งดั้งเดิมที่เป็นตัวเลข) default(T)เทียบเท่ากับ สตริงอาจจะถือว่าเป็นกรณีพิเศษที่เป็นสตริง / ช่องว่างที่ว่างเปล่าได้รับการปฏิบัติเช่นเดียวกับการเริ่มต้น (T) T userValue; var isBlank = (userValue is string) && String.IsNullOrWhitespace(userValue as string);ดังนั้นคุณอาจต้องการที่จะตรวจสอบว่า
Triynko

0

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

public class Foo <T> {

    T var;

    public <T> void doSomething(Class <T> cls) throws InstantiationException, IllegalAccessException {
        this.var = cls.newInstance();
    }

}

รหัสนี้จะรวบรวม (หมายเหตุ T ลบออกจากการประกาศวิธีการ):

public class Foo <T> {

    T var;

    public void doSomething(Class <T> cls) throws InstantiationException, IllegalAccessException {
        this.var = cls.newInstance();
    }

}

-5

เปลี่ยนบรรทัดนี้:

if (typeof(T) == typeof(string))

สำหรับบรรทัดนี้:

if (t.GetType() == typeof(string))

1
พวกเขาเหมือนกัน
bigworld12

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