2010-12-10 28 views
20

Tôi đang cố gắng viết một phương thức trợ giúp có thể ghi thông báo và ném một ngoại lệ của một kiểu được chỉ định với cùng một thông báo. Tôi có những điều sau đây:Cách tạo một thể hiện của một đối số kiểu chung bằng cách sử dụng một hàm tạo tham số trong C#

private void LogAndThrow<TException>(string message, params object[] args) where TException : Exception, new() 
{ 
    message = string.Format(message, args); 
    Logger.Error(message); 
    throw new TException(message); 
} 

Trước khi thêm các hạn chế new() trình biên dịch phàn nàn rằng nếu không có nó tôi không thể nhanh chóng TException. Bây giờ thông báo lỗi tôi nhận được là "Không thể cung cấp các đối số khi tạo một thể hiện của tham số kiểu 'TException'". Tôi đã cố gắng tạo ra các dụ với các constructor parameterless và sau đó thiết lập các tài sản Message nhưng nó chỉ đọc.

Đây có phải là giới hạn của ngôn ngữ hoặc có giải pháp mà tôi không biết? Có lẽ tôi có thể sử dụng sự phản chiếu nhưng đó là quá mức cần thiết cho một nhiệm vụ đơn giản như vậy. (Và khá xấu xí, nhưng đó là vấn đề cá nhân.)

+1

thể trùng lặp của [Tạo thể hiện của loại generic ?] (http://stackoverflow.com/questions/731452/create-instance-of-generic-type) – nawfal

Trả lời

13

Bạn có thể sử dụng Activator.CreateInstance() (cho phép bạn chuyển đối số) để tạo một phiên bản TException. Sau đó, bạn có thể ném số TException đã tạo.

Ví dụ:

private void LogAndThrow<TException>(string message, params object[] args) where TException : Exception, new() 
{ 
    message = string.Format(message, args); 
    Logger.Error(message); 

    TException exception = (TException)Activator.CreateInstance(typeof(TException), message); 
    throw exception; 
} 
+0

Tôi đã đánh dấu điều này là được chấp nhận vì giải pháp của JaredPar có thể nhanh hơn nhưng giải pháp này kết thúc tốt đẹp với hackery bên trong phương thức và thanh lịch hơn từ quan điểm của người gọi. – neo2862

4

Đây là một hạn chế của các hạn chế chung new. Nó chỉ có thể được sử dụng để tạo các đối tượng thông qua hàm tạo parameterless.

Một cách để giải quyết vấn đề này là cung cấp một phương thức nhà máy lambda có các thông số thích hợp. Tại trang web cuộc gọi, nó có thể trì hoãn việc xây dựng lớp học

private void LogAndThrow<TException>(
    Func<string,TException> func, 
    string message, 
    params object[] args) where TException : Exception {  

    message = string.Format(message, args);  
    Logger.Error(message); 
    throw func(message); 
} 

LogAndThrow(msg => new InvalidOperationException(msg), "my message"); 
8

Vâng, đó là một hạn chế; không có cấu trúc ngôn ngữ cho điều đó.

Đề xuất của tôi trong trường hợp này là tạo một số được nhập ủy quyền cho hàm tạo cho mỗi loại; bộ nhớ cache mà đại biểu (thường là trong một trường tĩnh của một loại chung, để thuận tiện) và tái sử dụng nó. Tôi có thể cung cấp một ví dụ sau - nhưng tôi không thể làm điều đó từ iPod;)

Tôi tin rằng Tôi đã cam kết một số mã này vào thư viện MiscUtil của Jon Skeet; vì vậy bạn cũng có thể nhìn ở đó.


Theo yêu cầu (bình luận), đây là một cách để làm điều này - trong trường hợp này bằng cách sử dụng Expression API. Lưu ý đặc biệt việc sử dụng các lớp generic lồng nhau để đảm bảo chúng ta làm sự phản ánh/biên soạn cùng một lúc nhất cho mỗi kiểu kết hợp:

using System; 
using System.Linq.Expressions; 

class Program { 
    static void Main() { 
     var ctor = TypeFactory.GetCtor<int, string, DemoType>(); 

     var obj = ctor(123, "abc"); 
     Console.WriteLine(obj.I); 
     Console.WriteLine(obj.S); 
    } 
} 

class DemoType { 
    public int I { get; private set; } 
    public string S { get; private set; } 
    public DemoType(int i, string s) { 
     I = i; S = s; 
    } 
} 

static class TypeFactory { 
    public static Func<T> GetCtor<T>() { return Cache<T>.func; } 
    public static Func<TArg1, T> GetCtor<TArg1, T>() { return Cache<T, TArg1>.func; } 
    public static Func<TArg1, TArg2, T> GetCtor<TArg1, TArg2, T>() { return Cache<T, TArg1, TArg2>.func; } 
    private static Delegate CreateConstructor(Type type, params Type[] args) { 
     if(type == null) throw new ArgumentNullException("type"); 
     if(args == null) args = Type.EmptyTypes; 
     ParameterExpression[] @params = Array.ConvertAll(args, Expression.Parameter); 
     return Expression.Lambda(Expression.New(type.GetConstructor(args), @params), @params).Compile(); 

    } 
    private static class Cache<T> { 
     public static readonly Func<T> func = (Func<T>)TypeFactory.CreateConstructor(typeof(T)); 
    } 
    private static class Cache<T, TArg1> { 
     public static readonly Func<TArg1, T> func = (Func<TArg1, T>)TypeFactory.CreateConstructor(typeof(T), typeof(TArg1)); 
    } 
    private static class Cache<T, TArg1, TArg2> { 
     public static readonly Func<TArg1, TArg2, T> func = (Func<TArg1, TArg2, T>)TypeFactory.CreateConstructor(typeof(T), typeof(TArg1), typeof(TArg2)); 
    } 
} 
+0

Bạn có thể cho tôi biết bạn sẽ làm điều đó như thế nào? Nghe có vẻ rất thú vị. – neo2862

+0

@ neo2862 có thể thông qua API biểu thức - Tôi sẽ ở PC trong khoảng 2 giờ; sẽ đăng sau đó –

+0

@ neo2862 thêm ngay bây giờ ... –

1

thử này

private void ThrowAndLog<TException>(string message, params object[] args) 
    where TException : Exception, new() 
{ 
    message = string.Format(message, args); 
    Logger.Error(message); 
    throw (TException)typeof(TException).GetConstructor(
     new[] { typeof(string) }).Invoke(new object[] { message }); 
} 
+0

Chung là để tăng tốc thời gian chạy, không thực hiện tạo thời gian chạy. –

Các vấn đề liên quan