Truyền đối tượng tới T


90

Tôi đang phân tích cú pháp một tệp XML với XmlReaderlớp trong .NET và tôi nghĩ sẽ thật thông minh nếu viết một hàm phân tích cú pháp chung để đọc các thuộc tính khác nhau một cách chung chung. Tôi đã nghĩ ra chức năng sau:

private static T ReadData<T>(XmlReader reader, string value)
{
    reader.MoveToAttribute(value);
    object readData = reader.ReadContentAsObject();
    return (T)readData;
}

Khi tôi nhận ra, điều này không hoàn toàn hoạt động như tôi đã lên kế hoạch; nó tạo ra một lỗi với các kiểu nguyên thủy chẳng hạn như inthoặc double, vì kiểu ép kiểu không thể chuyển đổi từ kiểu a stringsang kiểu số. Có cách nào để chức năng của tôi chiếm ưu thế ở dạng đã sửa đổi không?

Câu trả lời:


207

Đầu tiên hãy kiểm tra xem nó có thể được cast hay không.

if (readData is T) {
    return (T)readData;
} 
try {
   return (T)Convert.ChangeType(readData, typeof(T));
} 
catch (InvalidCastException) {
   return default(T);
}

1
Tôi đã thay đổi dòng với Convert.ChangeType thành: 'return (T) Convert.ChangeType (readData, typeof (T), System.Globalization.CultureInfo.InstalledUICulture.NumberFormat) để làm cho nó hoạt động trên nhiều cấu hình văn hóa khác nhau.
Kasper Holdum

2
Đây là câu trả lời chính xác. Nhưng tôi có thể lập luận rằng thử / bắt là hoàn toàn thừa ở đây. Đặc biệt là xem xét ngoại lệ tắt tiếng. Tôi nghĩ rằng phần if (readData là T) {...} là một nỗ lực đủ.
pimbrouwers

Bạn có thể kiểm tra xem readDate có trống không trước khi chuyển đổi nó. Nếu vậy trả về mặc định (T).
Manuel Koch

Tôi nhận được "Đối tượng phải triển khai IConvertible."
Casey Crookston

19

Bạn đã thử Convert.ChangeType chưa?

Nếu phương thức luôn trả về một chuỗi, mà tôi thấy kỳ lạ, nhưng đó là điều không cần thiết, thì có lẽ mã đã thay đổi này sẽ làm những gì bạn muốn:

private static T ReadData<T>(XmlReader reader, string value)
{
    reader.MoveToAttribute(value);
    object readData = reader.ReadContentAsObject();
    return (T)Convert.ChangeType(readData, typeof(T));
}

Ban đầu tôi đã xem qua Convert.ChangeType nhưng quyết định rằng nó không hữu ích cho hoạt động này vì một số lý do ngớ ngẩn. Cả bạn và Bob đều đưa ra câu trả lời giống nhau và tôi quyết định kết hợp giữa các câu trả lời của bạn nên tôi tránh sử dụng câu lệnh try nhưng vẫn sử dụng 'return (T) readData' khi có thể.
Kasper Holdum

11

thử

if (readData is T)
    return (T)(object)readData;

3

Bạn có thể yêu cầu loại là loại tham chiếu:

 private static T ReadData<T>(XmlReader reader, string value) where T : class
 {
     reader.MoveToAttribute(value);
     object readData = reader.ReadContentAsObject();
     return (T)readData;
 }

Và sau đó thực hiện một thao tác khác sử dụng các loại giá trị và TryParse ...

 private static T ReadDataV<T>(XmlReader reader, string value) where T : struct
 {
     reader.MoveToAttribute(value);
     object readData = reader.ReadContentAsObject();
     int outInt;
     if(int.TryParse(readData, out outInt))
        return outInt
     //...
 }

3

Trên thực tế, vấn đề ở đây là việc sử dụng ReadContentAsObject. Thật không may, phương pháp này không đáp ứng được mong đợi của nó; trong khi nó sẽ phát hiện loại phù hợp nhất cho giá trị, nó thực sự trả về một chuỗi, bất kể điều gì (điều này có thể được xác minh bằng cách sử dụng Reflector).

Tuy nhiên, trong trường hợp cụ thể của bạn, bạn đã biết loại bạn muốn truyền đến, do đó tôi sẽ nói rằng bạn đang sử dụng phương pháp sai.

Thay vào đó, hãy thử sử dụng ReadContentAs, nó chính xác là thứ bạn cần.

private static T ReadData<T>(XmlReader reader, string value)
{
    reader.MoveToAttribute(value);
    object readData = reader.ReadContentAs(typeof(T), null);
    return (T)readData;
}

Trông khá nhỏ gọn và trang nhã. Tuy nhiên, giải pháp trong câu trả lời được chấp nhận sử dụng ChangeType tương thích với nhiều nền văn hóa khác nhau vì nó chấp nhận IFormatProvider. Vì đây là điều cần thiết cho dự án nên tôi sẽ giữ nguyên giải pháp đó.
Kasper Holdum 07-07-09

2

Có lẽ bạn có thể chuyển vào, dưới dạng một tham số, một đại biểu sẽ chuyển đổi từ chuỗi thành T.


1

Thêm ràng buộc 'lớp' (hoặc chi tiết hơn, như lớp cơ sở hoặc giao diện của các đối tượng T được miễn trừ của bạn):

private static T ReadData<T>(XmlReader reader, string value) where T : class
{
    reader.MoveToAttribute(value);
    object readData = reader.ReadContentAsObject();
    return (T)readData;
}

hoặc where T : IMyInterfacehoặc where T : new(), v.v.


1

Trên thực tế, các câu trả lời đưa ra một câu hỏi thú vị, đó là những gì bạn muốn chức năng của mình thực hiện trong trường hợp có lỗi.

Có lẽ sẽ có ý nghĩa hơn nếu xây dựng nó dưới dạng một phương thức TryParse cố gắng đọc thành T, nhưng trả về false nếu không thể thực hiện được?

    private static bool ReadData<T>(XmlReader reader, string value, out T data)
    {
        bool result = false;
        try
        {
            reader.MoveToAttribute(value);
            object readData = reader.ReadContentAsObject();
            data = readData as T;
            if (data == null)
            {
                // see if we can convert to the requested type
                data = (T)Convert.ChangeType(readData, typeof(T));
            }
            result = (data != null);
        }
        catch (InvalidCastException) { }
        catch (Exception ex)
        {
            // add in any other exception handling here, invalid xml or whatnot
        }
        // make sure data is set to a default value
        data = (result) ? data : default(T);
        return result;
    }

chỉnh sửa: bây giờ tôi nghĩ về nó, tôi có thực sự cần thực hiện kiểm tra convert.changetype không? không phải dòng as đã cố gắng làm điều đó? Tôi không chắc rằng việc thực hiện lệnh gọi thay đổi bổ sung đó có thực sự đạt được bất cứ điều gì hay không. Trên thực tế, nó có thể chỉ làm tăng chi phí xử lý bằng cách tạo ra ngoại lệ. Nếu có ai biết về sự khác biệt đáng để làm, hãy đăng bài!

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.