Giá trị của loại 'T' không thể được chuyển đổi thành


146

Đây có thể là một câu hỏi mới, nhưng google ngạc nhiên không cung cấp câu trả lời.

Tôi có phương pháp khá giả tạo này.

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

    return t;
}

Đến từ một nền tảng C ++, tôi đã mong đợi nó hoạt động. Tuy nhiên, nó không thể biên dịch với "Không thể chuyển đổi hoàn toàn loại 'T' thành chuỗi" và "Không thể chuyển đổi loại 'T' thành chuỗi" cho cả hai bài tập trên.

Tôi hoặc đang làm một cái gì đó sai về mặt khái niệm hoặc chỉ có cú pháp sai. Xin hãy giúp tôi sắp xếp cái này ra.

Cảm ơn bạn!


20
IMO, nếu bạn đang kiểm tra các loại trong mã tổng quát của mình, thì thuốc generic có thể không phải là giải pháp chính xác cho vấn đề của bạn.
Austin Salonen

Biểu thức typeof(T) == typeof(string)được giải quyết trong thời gian chạy, không phải thời gian biên dịch. Do đó, dòng sau trong khối là không hợp lệ.
Steve Guidi

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

2
@vsapiha, Chỉ hoạt động nếu đối tượng triển khai IConvertible. Ngọt ngào nếu nó làm mặc dù.
ngerak

Câu trả lời:


285

Mặc dù nó nằm trong một ifkhối, trình biên dịch không biết đó Tstring.
Vì vậy, nó không cho phép bạn đúc. (Cũng vì lý do tương tự mà bạn không thể cast DateTimeđến string)

Bạn cần truyền tới object, (cái nào Tcũng có thể truyền tới), và từ đó đến string(kể từobject có thể chuyển sang string).
Ví dụ:

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

2
Những công việc này! Tôi đoán lần thứ hai cũng nên là T newT2 = (T) (object) t; mặc dù đó không phải là một op.
Alex

2
Bổ sung: Các mẫu C ++ về cơ bản là cắt và dán tại thời điểm biên dịch với các giá trị chính xác được thay thế. Trong C #, mẫu chung thực tế (không phải là "khởi tạo" của nó) tồn tại sau khi biên dịch và do đó phải (bỏ qua cách chơi chữ) phải chung chung trong giới hạn loại đã chỉ định.

(chuỗi) (đối tượng) t; mặc dù không có gì ở đây, cũng có thể bỏ qua điều đó, (chuỗi) (đối tượng) đó là
Doggett

6
Tại sao không chỉ sử dụng "như chuỗi"? Ví dụ: phần này biên dịch tốt (tôi thực sự chỉ biên dịch nó mà không có lỗi) khi userDefinedValue thuộc loại T:var isBlank = (userDefinedValue is string) && String.IsNullOrWhiteSpace(userDefinedValue as string);
Triynko

1
Điều này cảm thấy như một sai lầm của các nhà thiết kế trình biên dịch. Nếu tất cả T có thể được truyền một cách rõ ràng vào đối tượng và tất cả các đối tượng có thể được truyền thành chuỗi một cách rõ ràng thì nên tồn tại một quy tắc chuyển tiếp mà T có thể được truyền thành chuỗi rõ ràng. Nếu bạn thực hiện không chính xác diễn viên thì sẽ xảy ra lỗi thời gian chạy.
P.Brian.Mackey

10

Cả hai dòng có cùng một vấn đề

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

Trình biên dịch không biết rằng T là một chuỗi và vì vậy không có cách nào để biết cách gán chuỗi đó. Nhưng vì bạn đã kiểm tra nên bạn có thể buộc nó bằng

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

bạn không cần truyền t vì nó đã là một chuỗi, cũng cần thêm ràng buộc

where T : class

2
Sai lầm. Điều này sẽ không biên dịch. Xem câu trả lời của tôi.
SLaks

2
Biên dịch tốt (với vị trí đó, được thêm vào rằng một vài giây sau khi tôi đăng, có thể đã bỏ lỡ điều đó). Rất tiếc, quên thay đổi dàn diễn viên
Doggett

2

Tôi biết mã tương tự mà OP đã đăng trong câu hỏi này từ các trình phân tích cú pháp chung. Từ góc độ hiệu suất, bạn nên sử dụng Unsafe.As<TFrom, TResult>(ref TFrom source), có thể được tìm thấy trong gói System.R nb.CompilerService.Unsafe NuGet. Nó tránh quyền anh cho các loại giá trị trong các kịch bản này. Tôi cũng nghĩ rằng Unsafe.Askết quả là mã JIT được sản xuất ít hơn so với truyền hai lần (sử dụng (TResult) (object) actualString), nhưng tôi đã không kiểm tra điều đó.

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 sẽ được thay thế bằng JIT bằng các hướng dẫn mã máy hiệu quả, như bạn có thể thấy trong repo CoreFX chính thức:

Mã nguồn của không an toàn.


1

Nếu bạn đang kiểm tra các loại rõ ràng, tại sao bạn lại khai báo các biến đó là 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
Dòng thứ hai tạo ra một biến loại T.
SLaks

Bạn hỏi tại sao phải kiểm tra loại? Giả sử bạn có một loại trường cơ sở lưu trữ các objectgiá trị, với các loại dẫn xuất lưu trữ stringcác giá trị. Giả sử các trường này cũng có giá trị "Default IfNotProvided", do đó bạn cần kiểm tra xem giá trị do người dùng cung cấp (có thể là một đối tượng hoặc một chuỗi hoặc thậm chí là một số nguyên thủy) có tương đương với không default(T). Chuỗi có thể được coi là trường hợp đặc biệt trong đó chuỗi trống / khoảng trắng được xử lý giống như mặc định (T), vì vậy bạn có thể muốn kiểm tra xem T userValue; var isBlank = (userValue is string) && String.IsNullOrWhitespace(userValue as string);.
Triynko

0

Bạn cũng sẽ gặp lỗi này nếu bạn có một khai báo chung cho cả lớp và phương thức của bạn. Ví dụ mã hiển thị dưới đây đưa ra lỗi biên dịch này.

public class Foo <T> {

    T var;

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

}

Mã này không biên dịch (lưu ý T loại bỏ khỏi khai báo phương thức):

public class Foo <T> {

    T var;

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

}

-5

Thay đổi dòng này:

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

Đối với dòng này:

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

1
chúng giống nhau
bigworld12

Cả hai đều giống nhau ... chỉ sử dụng từ khóa ngôn ngữ so với sử dụng API thư viện lớp.
Abdulhameed
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.