Không thể chuyển đổi hoàn toàn loại 'Int' thành 'T'


90

Tôi có thể gọi Get<int>(Stat);hoặcGet<string>(Name);

Nhưng khi biên dịch tôi nhận được:

Không thể chuyển đổi hoàn toàn kiểu 'int' thành 'T'

và điều tương tự đối với 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
Có thể bạn đang nghĩ rằng khối if đã kiểm tra rằng T là int, vì vậy trong khối, bạn biết T là int và bạn sẽ có thể chuyển đổi ngầm int thành T. Nhưng trình biên dịch không được thiết kế để tuân theo suy luận đó, nó chỉ biết nói chung T không bắt nguồn từ int, vì vậy nó không cho phép chuyển đổi ngầm định. (Và nếu trình biên dịch hỗ trợ nó, người xác minh sẽ không, do đó biên soạn lắp ráp sẽ chưa được kiểm chứng.)
JGWeissman

Câu trả lời:


132

Bất cứ khi nào bạn thấy mình đang bật một kiểu trong một kiểu nói chung thì gần như chắc chắn bạn đang làm sai . Thuốc generic phải là generic ; chúng sẽ hoạt động giống hệt nhau hoàn toàn độc lập với loại .

Nếu T chỉ có thể là int hoặc string thì ngay từ đầu đừng viết mã của bạn theo cách này. Viết hai phương thức, một phương thức trả về giá trị int và phương thức trả về chuỗi ký tự.


1
Lấy <Xe> nơi ô tô lắp IConvertible sẽ gây ra gãy. Khi ai đó thấy bạn có một phương pháp chung, họ sẽ cho rằng họ có thể truyền vào bất cứ thứ gì thực hiện IConvertible.
Tjaart

10
Tôi chỉ có thể đồng ý một phần với bạn, @Eric. Tôi có một tình huống mà tôi phải phân tích cú pháp các mảng được lưu trữ trong các thẻ XML. Vấn đề là thông số kỹ thuật mà tài liệu XML tuân theo (trong trường hợp của tôi là COLLADA) nói rằng các mảng đó có thể không chỉ float, int và bool mà còn một số kiểu tùy chỉnh.Tuy nhiên, trong trường hợp bạn nhận được float [] (các thẻ mảng chứa kiểu dữ liệu được lưu trữ trong tên của chúng: float_array store float), bạn cần phải phân tích cú pháp chuỗi thành một mảng float, đòi hỏi phải sử dụng một số IFormatProvider). Rõ ràng là tôi không thể sử dụng "T.Parse (...)". Vì vậy, đối với một số ít trường hợp, tôi cần sử dụng chuyển đổi như vậy.
rbaleksandar,

1
Câu trả lời này sẽ giúp bạn thoát khỏi lỗ thỏ. Tôi muốn tạo một hàm chung cho int, int?, bool, bool?, string, và điều đó dường như là không thể.
Jess

Điều này làm cho việc chuyển đổi trên kiểu liệt kê chung trở nên thực tế.
David A. Gray

1
Tôi không muốn sử dụng điều này làm câu trả lời. Nhưng anh ấy đúng. Tôi muốn kiểm tra loại và, nếu một loại cụ thể, hãy đặt một thuộc tính trên đó. Giải pháp là tạo một phương thức có tham số được nhập mạnh.
Matt Dawdy

140

Bạn có thể chỉ sử dụng Convert.ChangeType()thay vì mã tùy chỉnh của mình:

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

21
Làm thế nào vềreturn (T)(object)PlayerStats[type];
maxp

11
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;vì không cần kiểm tra null.
BoltClock

Điều này ở trên sẽ không biên dịch cho tôi. T cần phải là một kiểu tham chiếu để biên dịch "as".
Robert Schmidt

9

ChangeTypecó lẽ là lựa chọn tốt nhất của bạn. Giải pháp của tôi tương tự như giải pháp được cung cấp bởi BrokenGlass với một chút logic thử bắt.

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;
}

Trường hợp sử dụng của tôi là một lớp cụ thể có nguồn gốc từ một lớp trừu tượng chung. Lớp được đánh dấu là trừu tượng vì nó định nghĩa một phương thức trừu tượng hoạt động trên thành viên private chung của lớp cơ sở. Generic sử dụng ràng buộc Enum C # 7.3 trên kiểu generic của nó. Tôi vừa hoàn thành thành công một bài kiểm tra và nó hoạt động chính xác như tôi hy vọng.
David A. Gray,

8

Thử đi:

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];
    }
}

Cảm ơn bạn điều này đã giúp, nhu cầu của tôi là khác nhau. Tôi đang viết một phương thức giả cho một phương thức tĩnh hiện có để tôi có thể kiểm tra nó. Sử dụng osherove này.com/blog/2012/7
Esen

8

Trên thực tế, bạn chỉ có thể chuyển đổi nó thành objectvà sau đó sang T.

T var = (T)(object)42;

Một ví dụ cho 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
    }
}

Đôi khi, hành vi này là mong muốn. Ví dụ: khi triển khai hoặc ghi đè một phương thức chung từ lớp cơ sở hoặc giao diện và bạn muốn thêm một số chức năng khác nhau dựa trên Tkiểu.


6

Xem xét logic @BrokenGlass ( Convert.ChangeType) không hỗ trợ kiểu GUID.

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

Lỗi : Truyền từ 'System.String' sang 'System.Guid' không hợp lệ.

Thay vào đó, hãy sử dụng logic bên dưới bằng TypeDescriptor.GetConvertercách thêm System.ComponentModelkhông gian tên.

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

Đọc cái này .



0

Bạn có thể chỉ cần truyền như dưới đây,

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;
 }
}
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.