2010-09-21 29 views
15

Vì vậy, tôi có một thuộc tính tùy chỉnh, chúng ta hãy gọi nó MyCustomAttribute, trong đó có một nhà xây dựng như sau:int Nullable trong Thuộc tính Constructor hoặc tài sản

public MyCustomAttribute(int? i) 
{ 
    // Code goes here 
} 

và tuyên bố một tài sản:

public int? DefaultProperty { get; set; } 

Bây giờ nếu Tôi muốn sử dụng tài sản, tôi cần phải chuyển vào một số int hoặc null, đó là những gì tôi mong đợi.

Nhưng điều này đưa ra một lỗi biên dịch:

[MyCustomAttribute(1, DefaultProperty = 1)] 
public int? MyProperty { get; set; } 

và vì vậy thực hiện điều này:

[MyCustomAttribute(null,DefaultProperty = null)] 
public int? MyProperty { get; set; } 

Lỗi này là: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type cho cả các nhà xây dựng và bất động sản.

Tại sao điều này? Nếu tôi thay đổi các nhà xây dựng để có một int, tôi có thể vượt qua trong 0, nhưng không null, trong đó loại đánh bại mục đích của giá trị (mà đôi khi có thể null)

+1

Trường hợp nào bạn đề cập đến trong tiêu đề? (Một int vô giá trị không phải là một enum.) – dtb

+2

Bạn có thể cung cấp hai hàm tạo: một với một tham số int và một tham số không có tham số. – dtb

+0

Vâng xin lỗi, nghĩ rằng nó đã được enum lúc đầu tiên, hóa ra đó là vô giá int – PostMan

Trả lời

19

Lý do tại sao là trong khi cả hai 0null là hằng số biểu thức thực tế cần thiết để khởi tạo tham số constructor là không. Cả hai yêu cầu chuyển đổi phải là biểu thức int? hợp lệ. Dưới mui xe nó về cơ bản tạo ra sau

[MyCustomAttribute(new Nullable<int>(0))] 

biểu hiện này không được liên tục là không hợp pháp như một cuộc tranh cãi thuộc tính

EDIT

DTB hỏi nếu điều này là bất hợp pháp trong các giá trị thuộc tính tại sao nó hợp pháp để có một tham số mặc định cho một tham số nullable?

void Example(int? x = null); 

Điều bạn cần xem xét là ai/cái gì diễn giải các giá trị.

Đối với các đối số thuộc tính giá trị được CLR giải thích. Các quy tắc liên quan đến các giá trị thuộc tính pháp lý đã không thực sự thay đổi kể từ 1.0. Nullable không tồn tại sau đó do đó CLR không hiểu mẫu khởi tạo nullable.

Đối với các đối số mặc định, vaule được diễn giải bởi trình biên dịch tại trang gọi của phương thức. Các trình biên dịch hiểu các giá trị nullable và có nhiều không gian hơn để làm việc với vì nó có thể tạo các biểu thức không liên tục tại trang gọi dựa trên các giá trị trong IL.

Thực tế nó hoạt động như thế nào? Đầu tiên trình biên dịch sẽ thực sự mã hóa hằng số và null khác nhau trong IL

// x = 0 
[DefaultParameterValue(0)] 
// x = null 
[DefaultParameterValue(null)] 

Tại trang web gọi trình biên dịch kiểm tra các giá trị và tạo ra (không liên tục) biểu hiện phù hợp với các giá trị tham số.

+0

Nhưng tại sao bạn có thể có 'int? x = null 'trong chữ ký phương thức? – dtb

+1

@ dtb lý do tại sao giá trị đối số mặc định được đánh giá bởi đối số trình biên dịch và thuộc tính của CLR. Trình biên dịch C# (và Vb.Net) có logic đặc biệt để làm cho các giá trị đối số mặc định làm việc cho các giá trị null nhưng CLR thì không. – JaredPar

+0

Nhưng không phải là 'int? x = 0 'được biên dịch thành [DefaultParameterValue (new Nullable (0))]? – dtb

4

Quy tắc chung để sử dụng thuộc tính là loại tham số mà chúng có thể được khai báo với từ khóa const - tức làtất cả các loại tiểu học (int, char, v.v.) và string. Không new từ khóa có thể được sử dụng trong thuộc tính thông số danh sách

Bạn không thể sử dụng một thuộc tính như thế này:

[Custom(new object())] 
class Class { 
} 

hoặc thậm chí như thế này (ngay cả khi DateTime là một kiểu giá trị):

[Custom(new DateTime(2001,1,1))] 
class Class { 
} 

Do đó, việc sử dụng Nullable<T> cũng không được phép, bởi vì vượt qua null hoặc 1 tương đương với việc này:

[Custom(new Nullable<int>())] 
//[Custom(null)] 
class Class { 
} 
[Custom(new Nullable<int>(1))] 
//[Custom(1)] 
class Class { 
} 
1

Tôi biết rằng đây là một câu hỏi cũ, nhưng không ai thực sự trả lời lý do tại sao Nullable <> không được phép. câu trả lời thuyết phục này này nằm trong the documentation for compiler error CS0655:

trang

Positional and named parameters for an attribute class are limited to the following attribute parameter types:

  • One of the following types: bool, byte, char, double, float, int, long, sbyte, short, string, uint, ulong, or ushort.
  • The type object.
  • The type System.Type.
  • An enum type, provided that it has public accessibility, and that any types in which it is nested also have public accessibility.
  • Single-dimensional arrays of the preceding types.

tài liệu hướng dẫn này là dành cho Visual Studio 2008, nhưng tôi đã không nghe nói về bất kỳ thay đổi gần đây trong lĩnh vực này.

+0

Điều này không trả lời tại sao. – frenchone

+0

@frenchone Tôi nghĩ rằng nó giải đáp tại sao mã của OP không biên dịch, nhưng không có nó không trả lời cấp độ tiếp theo của 'why', đó là 'tại sao các nhà biên dịch đưa ra quyết định này khi họ viết spec'. – bart

10

Tôi biết câu hỏi là lý do tại sao, nhưng đối với nhà phát triển chuyển hướng đến trang này và đang tìm kiếm câu trả lời. Đây là cách giải quyết của tôi.

public class MyCustomAttribute: Attribute 
{ 
    private Nullable<bool> myBoolean = null; 

    public bool MyBooleanProperty { get { return this.myBoolean.GetValueOrDefault(); } set { this.myBoolean = value; } } 
    public bool IsMyBooleanPropertySpecified { get { return this.myBoolean != null; } } 
} 
+0

Tốt hơn nên sử dụng phương thức GetValueOrDefault() (http://msdn.microsoft.com/en-us/library/72cec0e0(v=vs.110).aspx) trong getter thay vì .Value; bởi vì nếu myBoolean là null .Value sẽ ném một ngoại lệ. –

+0

Vâng, đó là một bổ sung tốt cho mã. Thnx! –

2

Tôi nghĩ sẽ dễ dàng quên kiểm tra IsPropertyXSet trước khi truy cập biến. Tôi nghĩ rằng việc sử dụng một nullable là tốt hơn. Vì vậy, đây là một cách tốt để cấu trúc mà trong khi vẫn giữ nullable:

public class FooAttribute : Attribute { 
     public bool? SomeFlag { get; set; } 

     public bool SetSomeFlag { 
     get { 
      throw new Exception("should not be called"); // required for property visibility 
     } 
     set { 
      SomeFlag = value; 
     } 
    } 
} 

[Foo(SetSomeFlag=true)] 
public class Person { 
} 

[Foo] 
public class Person2 { // SetSomeFlag is not set 
} 

bool? b1 = ((FooAttribute)typeof(Person).GetCustomAttributes(typeof(FooAttribute), false)[0]).SomeFlag; // b1 = true 
bool? b2 = ((FooAttribute)typeof(Person2).GetCustomAttributes(typeof(FooAttribute), false)[0]).SomeFlag; // b2 = null 
0

Một anh chàng mua một chiếc Ferrari, nhưng một số kẻ ngốc thiết lập các thống đốc (contraption giới hạn tốc độ của xe) đến 30 mph. Anh chàng không thể thay đổi thống đốc cho một cái gì đó hợp lý hơn, vì vậy anh ta tách nó ra. Bây giờ Ferrari có thể đi nhanh như động cơ có thể cơ bắp.

Microsoft không cho phép bạn sử dụng các giá trị vô hiệu làm thuộc tính thuộc tính tùy chỉnh. Tuy nhiên, Microsoft cho phép bạn sử dụng chuỗi, có thể là null, như thuộc tính tùy chỉnh thuộc tính. Vì vậy, tách ra giới hạn hoàn toàn. Thay thế của bạn nullable int với một chuỗi. Chắc chắn, một chuỗi thậm chí còn ít hạn chế hơn một giá trị null vì nó có thể có các giá trị không nguyên như "bob", nhưng đó là mức giá bạn trả cho Microsoft vặn các thuộc tính tùy chỉnh, một tính năng ngôn ngữ, cho một chi tiết thực hiện trong CLR nên không liên quan.

Đây là ví dụ của tôi.

public abstract class Contract : Attribute, IContract 
{ 
    public abstract void Check (Object root, String path, Object valueAtPath); 
} 

public sealed class DecimalPlacesContract : Contract 
{ 
    public String MinimumMantissaCount 
    { 
     get 
     { 
      return minimumMantissaCount?.ToString(); 
     } 

     set 
     { 
      minimumMantissaCount = value == null ? (int?) null : Int32.Parse(value); 
     } 
    } 

    public String MaximumMantissaCount 
    { 
     get 
     { 
      return maximumMantissaCount?.ToString(); 
     } 

     set 
     { 
      maximumMantissaCount = value == null ? (int?) null : Int32.Parse(value); 
     } 
    } 

    public String MinimumSignificantDigitCount 
    { 
     get 
     { 
      return minimumSignificantDigitCount?.ToString(); 
     } 

     set 
     { 
      minimumSignificantDigitCount = value == null ? (int?) null : Int32.Parse(value); 
     } 
    } 

    public String MaximumSignificantDigitCount 
    { 
     get 
     { 
      return maximumSignificantDigitCount?.ToString(); 
     } 

     set 
     { 
      maximumSignificantDigitCount = value == null ? (int?) null : Int32.Parse(value); 
     } 
    } 

    private int? minimumMantissaCount; 
    private int? maximumMantissaCount; 
    private int? minimumSignificantDigitCount; 
    private int? maximumSignificantDigitCount; 



    public override void Check (Object root, String path, Object valueAtPath) 
    { 
     decimal? value = valueAtPath as decimal?; 

     int mantissaCount = DecimalMisc.GetMantissaDigitCount(value ?? 0); 
     int significantDigitCount = DecimalMisc.GetSignificantDigitCount(value ?? 0); 

     if (value == null || 
      mantissaCount < minimumMantissaCount || 
      mantissaCount > maximumMantissaCount || 
      significantDigitCount < minimumSignificantDigitCount || 
      significantDigitCount > maximumSignificantDigitCount) 
     { 
      throw new ContractException(this, root, path, valueAtPath); 
     } 
    } 
} 

Dưới đây là cách sử dụng thuộc tính thuộc tính tùy chỉnh.

private class Dollar 
    { 
     [DecimalPlacesContract (MinimumMantissaCount = "0", MaximumMantissaCount = "2")] 
     public decimal Amount { get; set; } 
    } 

    private class DollarProper 
    { 
     [DecimalPlacesContract (MinimumSignificantDigitCount = "2", MaximumSignificantDigitCount = "2")] 
     public decimal Amount { get; set; } 
    } 

Chỉ cần thêm dấu ngoặc kép xung quanh các giá trị. Các thuộc tính không xác định mặc định là null.

Bất kỳ giá trị không đúng nào như "bob" sẽ khiến một FormatException bị ném khi bạn gọi GetCustomAttributes hoặc GetCustomAttribute ra khỏi thể hiện Type hoặc PropertyInfo. Chắc chắn, nó sẽ là tốt đẹp để có các kiểm tra thời gian biên dịch của int nullable, nhưng điều này là đủ tốt. Vì vậy, rip thống đốc ngay lập tức.

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