2012-04-02 24 views
6

Tôi đang xem cách ParameterInfo.IsOptional được định nghĩa (tôi đang thêm hỗ trợ tham số mặc định vào khung IOC nội bộ), và dường như với tôi rằng, khi đúng, không có đảm bảo rằng ParameterInfo.DefaultValue (hoặc thực sự ParameterInfo.RawDefaultValue) là thực sự các giá trị mặc định sẽ được áp dụng.Tham số mặc định và phản ánh: nếu ParameterInfo.IsOptional thì DefaultValue luôn đáng tin cậy?

Nếu bạn nhìn vào MSDN example given for IsOptional, có vẻ như IL có thể xác định tham số là tùy chọn nhưng không được cung cấp mặc định (với điều kiện là ParameterAttributes.HasDefault phải được cung cấp rõ ràng). I E. có khả năng dẫn đến một tình huống mà một kiểu tham số là, giả sử, Int32, ParameterInfo.IsOptional là đúng, nhưng ParameterInfo.DefaultValue là rỗng.

Ngôn ngữ của tôi là C#, do đó tôi có thể làm việc với những gì mà trình biên dịch sẽ thực hiện. Trên cơ sở đó tôi có thể có một thử nghiệm đơn giản như sau (parameter đây là một trường hợp ParameterInfo, và phương pháp này có nghĩa là để trở về một thể được sử dụng như là đối số thời gian chạy cho các tham số):

if(no_config_value) 
{ 
    if(!parameter.IsOptional) throw new InvalidOperationException(); 
    //it's optional, so read the Default 
    return parameter.DefaultValue; 
} 
else 
    return current_method_for_getting_value(); 

Nhưng tôi m nghĩ rằng một số ngôn ngữ (và tôi muốn có được quyền này ở cấp IL, thay vì chỉ dựa trên những gì một trình biên dịch cụ thể làm) có thể đặt onus trên người gọi để xác định giá trị mặc định sẽ được sử dụng, nếu có , default(parameter.ParameterType) sẽ cần phải theo thứ tự.

Đây là nơi thú vị hơn một chút, bởi vì DefaultValue, rõ ràng là DBNull.Value (theo the documentation for RawValue) nếu không có mặc định. Sẽ không tốt nếu tham số thuộc loại objectIsOptional==true!

Sau khi thực hiện đào hơn một chút, tôi hy vọng rằng cách đáng tin cậy để giải quyết việc này là để cơ thể đọc ParameterInfo.Attributes thành viên, đọc bitflags riêng đầu tiên để kiểm tra ParameterAttributes.Optionalsau đó séc ParameterAttributes.Default. Chỉ khi có cả hai, thì đọc ParameterInfo.DefaultValue sẽ chính xác.

Tôi sẽ bắt đầu viết mã và viết các bài kiểm tra, nhưng tôi hỏi với hy vọng rằng có ai đó có nhiều kiến ​​thức về IL hơn có thể xác nhận những nghi ngờ của tôi và hy vọng rằng điều này sẽ đúng cho bất kỳ IL- dựa trên ngôn ngữ (do đó tránh sự cần thiết phải giả lập vô số thư viện bằng các ngôn ngữ khác nhau!).

Trả lời

6

Câu trả lời ngắn cho câu hỏi của tôi là không - chỉ vì IsOptional là đúng không có nghĩa là DefaultValue thực sự sẽ chứa giá trị mặc định thực. Giả sử của tôi tiếp tục xuống trong văn bản câu hỏi là chính xác (và các tài liệu. Net không kinda giải thích điều này, trong một vòng-về cách). Về bản chất, nếu một mặc định tồn tại, thì người gọi nên sử dụng nó, nếu không người gọi sẽ cung cấp mặc định của nó. Các tham số của Attributes được sử dụng để tìm ra nếu một mặc định tồn tại.

Đây là những gì tôi đã làm.

Giả sử phương pháp sau đây tồn tại:

/* wrapper around a generic FastDefault<T>() that returns default(T) */ 
public object FastDefault(Type t) { /*elided*/ } 

Và sau đó được đưa ra một thông số cụ thể và từ điển của các giá trị tham số đã cung cấp (từ cấu hình):

public object GetParameterValue(ParameterInfo p, IDictionary<string, object> args) 
{ 
    /* null checks on p and args elided - args can be empty though */ 
    object argValue = null; 
    if(args.TryGetValue(p.Name, out argValue)) 
    return argValue; 
    else if(p.IsOptional) 
    { 
    //now check to see if a default is supplied in the IL with the method 
    if((p.Attributes & ParameterAttributes.HasDefault) == 
     ParameterAttributes.HasDefault) 
     return p.DefaultValue; //use the supplied default 
    else 
     return FastDefault(p.ParameterType); //use the FastDefault method 
    } 
    else //parameter requires an argument - throw an exception 
    throw new InvalidOperationException("Parameter requires an argument"); 
} 

Tôi đã sau đó kiểm tra logic này trên constructors và các phương pháp được viết như sau:

public class Test 
{ 
    public readonly string Message; 
    public Test(string message = "hello") { Message = message; } 
} 

IE, ở đâu một mặc định được cung cấp ngoài tham số là tùy chọn (chương trình chính xác rơi vào nhánh mà đạt tới cho ParameterInfo.DefaultValue).

Sau đó, trong câu trả lời cho một phần khác của câu hỏi của tôi, tôi nhận ra rằng trong C# 4, chúng tôi có thể sử dụng OptionalAttribute để tạo ra một tham số tùy chọn không có mặc định:

public class Test2 
{ 
    public readonly string Message; 
    public Test2([OptionalAttribute]string message) { Message = message; } 
} 

Một lần nữa, chương trình rơi một cách chính xác vào chi nhánh thực hiện phương thức FastDefault.

(Trong trường hợp này C# cũng sẽ sử dụng mặc định của loại như là đối số cho tham số này)

Tôi nghĩ rằng bao gồm tất cả - nó hoạt động độc đáo trên tất cả mọi thứ tôi đã cố gắng (tôi đã có niềm vui cố gắng để có được độ phân giải quá tải cảm giác chính xác như hệ thống IOC của tôi luôn luôn sử dụng tương đương với các đối số được đặt tên - nhưng thông số C# 4 đã giúp ở đó).

+0

Tôi sẽ kiểm tra 'Test2' một! – nawfal

+1

Cũng không phải tất cả các ngôn ngữ đều yêu cầu bạn chỉ định một giá trị mặc định trong chữ ký hàm khi khai báo một tham số tùy chọn. Tôi không có chuyên gia, nhưng dường như trong trường hợp F #, các giá trị mặc định được áp dụng trong phần thân hàm - lợi ích của việc bạn có thể giữ mặc định nhất quán khi bạn triển khai phiên bản mới của assembly (trong khi với C# và VB mô hình, bạn có thể biên dịch một thư viện với một mặc định khác nhau ngày hôm nay, nhưng hội đồng biên soạn với phiên bản của ngày hôm qua vẫn sẽ gửi mặc định cũ). Tài liệu F # là [ở đây] (http://msdn.microsoft.com/en-gb/library/dd233213.aspx) –

+0

... Vì vậy, ví dụ F # ​​cho chúng ta một mẫu tốt cho lý do tại sao bạn sử dụng mẫu '[Tùy chọn]' thay vì 'int p1 = 0' trong C#: có thể vì bạn luôn mong đợi mặc định là kiểu mặc định, và sau đó bạn sẽ áp dụng mặc định khác trong phần thân hàm. –

0

Như bạn đã nêu, có sự khác biệt và không đáng tin cậy. Vâng, .NET 4.5 có HasDefaultValue, mà kiểm tra nếu một tham số là tùy chọn (IsOptional) cũng có một giá trị mặc định (DefaultValue) - giống như

(p.Attributes & ParameterAttributes.HasDefault) == ParameterAttributes.HasDefault 

trong các phiên bản trước đó. Đó sẽ là cách tiếp cận chính xác. Một cách tiếp cận khác là thay thế giá trị mặc định không hợp lệ tùy thuộc vào giá trị không hợp lệ trong trường hợp này (khi tham số không phải là tùy chọn và khi tham số là tùy chọn nhưng không có giá trị mặc định). Đối với ví dụ, bạn chỉ có thể làm:

if(p.DefaultValue != DBNull.Value) 
{ 
    if(p.DefaultValue != Type.Missing) 
     return p.DefaultValue; //use the supplied default 
    else 
     return FastDefault(p.ParameterType); //use the FastDefault method 
} 
else //parameter requires an argument - throw an exception 
    throw new InvalidOperationException("Parameter requires an argument"); 

này hoạt động vì p.DefaultValueDBNull khi tham số là không bắt buộc và Type.Missing khi tham số tùy chọn nhưng không được cung cấp với giá trị mặc định.

Vì điều này không có giấy tờ, tôi không khuyên bạn nên sử dụng. Tốt hơn là thay thế p.DefaultValue != DBNull.Value bằng p.IsOptional. Thậm chí tốt hơn là thay thế p.DefaultValue != Type.Missing bằng những gì bạn đã trả lời: (p.Attributes & ParameterAttributes.HasDefault) == ParameterAttributes.HasDefault

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