다음을 통해 공유


Poll: Attribute Design

I would like to run a quick poll. Which design do you prefer for a set of related attributes and why?

Option #1: Several Attribute Types:

public class AaaaFooAttribute: Attribute { }

public class BbbbFooAttribute: Attribute { }

public class CcccFooAttribute: Attribute { }

public class DdddFooAttribute: Attribute { }

 

Option #2: One Attribute Type and One Enum

public class FooAttribute: Attribute {

     public FooAttribute(FooType type) {}

}

public enum FooType { Aaaa, Bbbb, Cccc, Dddd }

Comments

  • Anonymous
    August 24, 2006
    It depends on the relationship between the attributes. If they are all modifications to a single behavior, such as the case of an attribute MyWindowMode with the possible modifications of FullScreen, Minimize, Maximize, or Normal, then I prefer one attribute. But if the attributes represent two distinct behaviors, such as MyBinarySerializer or MyXmlSerializer, then two different attributes.

    My basic rule of thumb has been the following:
     If it would be implemented using two different methods, then use two different attributes.
     If it would be implemented using one method with a passed in flag, then use one attribute.

    My reasoning for this, is promote the natural flow of code reading. If attributes are too different than from surrounding code, then it because less efficient to read.
  • Anonymous
    August 24, 2006
    I recently had to make a similar decision in a SDK that I've been developing.  I started out with your option #1 because it seemed "prettier" at first.  I soon refactored it, however, to use the same form as your option #2.  I had a couple of reasons for doing so.  First, the constructors and named parameters for each of the attribute types ended up being identical, resulting in a lot of duplicated code in both the class definitions as well as any code which manipulates the attributes (which is bad for me, the SDK developer).  Second, I thought that each new class I define is yet another class for the client developer to remember exists.  It's probably easier to remember how to use one slightly more complicated constructor than multiple slightly less complicated constructors.  Intellisense also a factor here; it makes it easy for the developer to determine what type to pass as the next argument.  It's not nearly as easy to determine what type one should use to begin with.

    -Phil
  • Anonymous
    August 24, 2006
    #2
    In this case, less classes (and documentation and etc) to deal with
  • Anonymous
    August 24, 2006
    I think it is going to depend on the situation. Either or are fine but depends what you are going to use it for. For example the Windows Terrarium (Wish they would bring that back) from http://www.windowsforms.net/Applications/application.aspx?PageID=30&tabindex=8 allows you to build a simple dll that contained the logic for your insect plant whatever you put into this massive terrarium.
    Attributes like If it was a Plant or Animal, if it was an Animal was it a Herbavore or a Carnivore. Were like option 1 as they made sense  to be like that. However, Attributes for Carnivore Like Eyesight, Speed, etc were done like option two, which again made perfect sense as they are all grouped together. So I guess it is going to depend on what your doing. Some places doing things a certain way just make more sense.

  • Anonymous
    August 24, 2006
    The comment has been removed
  • Anonymous
    August 24, 2006
    The comment has been removed
  • Anonymous
    August 24, 2006
    The comment has been removed
  • Anonymous
    August 24, 2006
    I prefer option two because the attribute in question is likely to have a more general name, and therefore easier to find in intelisense.  I may not know to look for Aaaa, Bbbb, Cccc or Dddd, but I will know to look for FooAttribute, and parameter tips could help me with the parameter.
  • Anonymous
    August 24, 2006
    Kris, as long as Aaaa et al can really be sensibly enumerated - e.g. Lock > Read, Write, Shared, ... - I would use Option #2, mainly to group the attribute(s) together without having to stick to a strict naming convention, but also reduce clutter in the namespace. Having different classes linke Option #1 is IMO not important, unless the attributes are so different that it makes sense to inherit from one of them, or for one of them to inherit from the other. But really, this is a gray area, ot we would just have public class Attribute(HugeEnum type).
  • Anonymous
    August 24, 2006
    I prefer the enum-driven version.  It allows the developer to recall only FooAttribute, relying on intellisense to prompt for FooType.  Further, FooType can be expanded independently without spawning new ZzzzFooAttribute.

    Downside of enum-switching in general:  if the developer wanted to create a method that consumed a subset of FooAttribute, the rejection of ineligible instances is no longer a compile-time feature.  That is, all the information is contained in FooType, whose values are not inspected by the compiler.

    Of course, if we had value parameterization of generics, and generic attributes, I'd prefer:

    FooAttribute<value(FooType)>: Attribute { }

    [FooAttribute<FooType.Aaaa>()]
  • Anonymous
    August 24, 2006
    Is this poll open to MS people, or external only?

    Personally, I'd pick #2, for two reasons:

    1. Less namespace pollution. No matter how many variants there are, you're only creating two new types.

    2. More discoverability. With option #2, once I discover FooAttribute, IntelliSense will give me a list of all valid FooTypes.
  • Anonymous
    August 24, 2006
    I think that a more concrete example would be more useful; it is a little too abstract.
    However, here are my preferences.

    Neither.
    As you say, they are a "set of related attributes".
    Why not create a common base class and inherit from that?
    To rewrite your example...

       // Option #3
       public class FooAttribute : Attribute { }

       public class AaaaFooAttribute : FooAttribute { }
       public class BbbbFooAttribute : FooAttribute { }
       public class CcccFooAttribute : FooAttribute { }
       public class DdddFooAttribute : FooAttribute { }

    I think that this more clearly communicates the relatedness of the attributes.
    Also, it allows for easier future maintainability. You can create new related attributes at will.
    With the enum version, to add new types, you will need to modify / extend the current enumeration itself, as well as the single FooAttribute type. I think that we can do better than that.

    However, if I have to choose between the two options that you present, then I would favor Option #1. Granted that the example is abstract, but it seems to me that these are separate but related attributes. Therefore, as with Option #2, grouping them into a single attribute (FooAttribute) does not seem like a very clear design. And of course there is also the same future maintainability issue. Also, what exactly does the parameter provide? Again, it is not a very clear design.

    I am opposed to Option #2 due to 1) maintainability and 2) clarity of design. The only other option is Option #1, which isn't too bad. But if you permit other options, then I vote for my Option #3.
  • Anonymous
    August 24, 2006
    Hi Krzysztof. I would choose an option depending on the possibility that the attributes would be used together to decorate the same class. If the most common scenario is that the developer would use more than one of these attributes in the same class, I would choose Option #1, because the code would look cleaner with more than one attribute applied, I mean the attribute declaration would look smaller. But, if the most common case is that the developer would use only one of these attributes in a class, I would choose Option #2, because it would make the VS intellisense list of attributes smaller and the developer would naturally relate all the variations of the attribute with the single FooAttribute name.

    Julio.
  • Anonymous
    August 24, 2006
    Option 2. Why? Because of discoverability. All you have to remember with option 2 is one class. Intellisense will do the rest by pushing the enumeration in your face. Option 1 forces you to choose between several classes without Intellisense aid.
  • Anonymous
    August 24, 2006
    In many ways, it seems to me to be analogous to writing several methods versus one method with parameters. It depends! How related are the attributes? Enums are quite discoverable - all the options pop up when you type in the enum name. With multiple methods / attributes, it's harder to find the related group.

    If the different attributes / methods aren't closely related, then it would be wrong to conflate them into an enum.

    What about user extensibility? With methods / attributes, one can define new classes / descendants and add new methods / classes that look just like the old ones. With the enums, their baked in. This can be both good and bad - it's more encapsulated etc, less risk of confusion.

    Another aspect: types for a purpose versus flexible types. Sometimes, people who are beginners to a problem look for something which is the perfect solution. Trouble is, most people's problems are diverse. This can lead to a large number of "simple" types for solving each particular class of problem, but the overall result is that the solution space is more complex because there's more of it. Beginners and experts alike become frustrated; the first daunted, the other feeling the clutter. In this light, fewer types that are configurable sounds better - leading to the attributes with enum arguments approach. But then - only if it makes sense! A hint: if the attributes would probably start or end with a common prefix or suffix, then it would probably make more sense to use an enum instead.
  • Anonymous
    August 24, 2006
    I believe that it's more intuitive (in most cases) to go with #1. The enum might work if it's a set of some closely related optional behaviors, but The Class Is The Type, so having an enum to define type is a little awkward. I tend to shy away from enums that are named XxxType. Also, it would require a few more steps with IntelliSense to get your attribute in place.

    "DO use an enum to strongly type parameters, properties, and return values that represent sets of values." (From the Book!)

    IMO, there's no room in that sentence for strongly typed types. And btw, what's the default (zero) value of such an enum?

    A minor drawback with #1 is that it could possibly clutter the namespace, but since you probably know what you're looking for, it's not that big of a deal. And if some of the types are more "advanced" you could do away with them in another namespace. (The Book again...)
  • Anonymous
    August 24, 2006
    Oh, definately option #2. It just feels so much more organized.

    If the attributes are logically grouped together, i.e.they are all some kind of FooAttribute, then they should also be grouped together by more than just name.
  • Anonymous
    August 24, 2006
    I will prefer option 2, It gives logical grouping for related functionality which help in understanding and maintaining the code.It also help dynamically create types on run time.
    But there is one downside for option 2, you have make sure that whenever defination of enum changes you recompile your clients too which uses that enum, cause enum after compilation get converted to const and const get hard coded in IL.
  • Anonymous
    August 24, 2006
    The comment has been removed
  • Anonymous
    August 24, 2006
    Option #2. First of all, on a gut level, it feels more natural to model what is after all a parameterised design decision with a parameterised attribute. After a little thought, it occurs to me that the IntelliSense story is probably better too - you only have to remember one attribute name and you can then be prompted with possible parameter values, while with Option #1 you would either have to remember all the possible parameter values in order to start typing the right one, or scroll up and down a list to find what you're looking for.

    Option #1 has some superficial appeal, as at first glance it is less complex to use, but I think in practice this is probably not true.
  • Anonymous
    August 24, 2006
    It's hard to say. I would like to see the real names and more about what they do. If AAA or BBBB or CCC is the most important part of a class then I will go for option #1 but if their meaning is minor then I will go for option #2.
  • Anonymous
    August 24, 2006
    The comment has been removed
  • Anonymous
    August 24, 2006
    If only between these two options, I would choose #1.

    However, I might be tempted to design with a third option depending on a) if the derieved classes shared much common code and b) there was a need to (or value in) refering to the group of attributes by a common class definition.

    public abstract class FooBaseAttribute : Attribute { }
    public class AaaaFooAttribute: FooBaseAttribute { }
    public class BbbbFooAttribute: FooBaseAttribute { }
    public class CcccFooAttribute: FooBaseAttribute { }
    public class DdddFooAttribute: FooBaseAttribute { }
  • Anonymous
    August 25, 2006
    Hi Krzysztof,

    My initial reaction was to favour the second option due to its elegance, noting that this design implies all the related attributes must have the same set of properties over time.

    The first option does not have the same limitation as the second, however (taking the  example literally) if there are common properties between the related attributes, then these common properties must be duplicated across the attributes. This could be resolved with attribute inheritance, although this is rare in the BCL, perhaps for good reason (SoapExtensionAttribute is the only example that springs to mind).

    Its also worth considering the two usage scenarios for attributes; decalarative application and discovery. In terms of declarative application, it will be easier for the consumer to discover the available set of related attributes in the IDE using the second option (via Intellisense for the FooType enum). Implementation of discovery will also be cleaner (potentially involving a switch statement based on FooType). The first option will be more difficult for consumers to declaratively apply as its more difficult to discover the related attributes in the IDE, and will also be less elegant to discover (an if statement per related attribute is required).

    Summing up, if different related attributes need to have different sets of properties, then the first option is the only viable, if less elegant, solution. If this flexibility is not required, the second option is preferred in terms of elegance and use.

    Now all thats needed is a crystal ball :)

    Cheers,
    Matt
  • Anonymous
    August 25, 2006
    Option 1... it's much more clear and 2 doesn't really gain you anything.
  • Anonymous
    August 25, 2006
    In my oppinion the two options are applicable in different scenarios.
    Option #1 enables multiple attribute types to be used on a single attribute target:
    [AaaaFooAttribute]
    [BbbbFooAttribute]
    public class AttributeTarget{}

    Option #2, as you have written it, restricts the usage to:
    [FooAttribute(FooType.Aaaa)]
    public class AttributeTarget{}

    If the FooType enum was attributted with [Flags] then option #2 would be equivalent to option #1, and IMHO this would be an easier to understand design (and maybe easier to implement).

    In short, option #2 is my choice.

    Kind regards,
    Dimo
  • Anonymous
    August 25, 2006
    No way to decide without any contextual information -- what are the real names? How can we possibly decide without knowing what they are used for? It is similar to problem you would have when deciding between multiple classes or a single class and enum, in any scenario. Option 1 is shorter code but option2 easier to discover relationships, more logical.
  • Anonymous
    August 25, 2006
    In general I prefer option #2 assuming that the attributes are all related or a variation on the general type

    Imagine the PasswordPropertyTextAttribute class had the ability to specify the character used to obscure the text.  I think an enum for the type of charactersused to obscure the text eg
    public class PasswordPropertyTextAttribute: Attribute {
     public PasswordPropertyTextAttribute(CharacterType type) {}
    }
    public enum CharacterType { AsteriskCharacter, QuestionMarkCharacter, BlockCharacter }

    Would be better than
    public class AsteriskPasswordPropertyTextAttibute(), etc

    The reason being that I know I only need to look for one Attribute class, and the variation is controlled by the levers/enumerations around the attribute. If the variation was spread out among multiple classes, it would be more difficult to see all the variations. Start out with the main concept (attribute) and then drill down within it.

    I tried to think of a good example for option #1, but have yet to come up with one.
  • Anonymous
    August 26, 2006
    Too often do I hear the argument for syntax design due to Intellisense.
    Intellisense is NOT a feature of a language. It is not part of C#. Visual C#, an IDE, utilizes Intellisense.
    However, not everyone uses an IDE. Some use a plain text editor, such as Notepad.

    A common framework utilized by many different languages and many different editors should be designed on clarity of design, maintainability, readability, etc, not features specific to particular IDEs.
    I think it is a mistake to favor option #2 because of Intellisense assistance.
  • Anonymous
    August 27, 2006
    I'm not sure "the attributes are related" is enough to answer: knowing what really make these attributes different (and how different behaviors they could imply) should also usefull.

    By default, I prefer Option #1: in case of attributes, mixing responsibilities in the same class, even if the concerned attributes are related won't be my first choice.

    Eric

  • Anonymous
    August 28, 2006
    I would suggest a mix between the two options with a little change.
    Where we'll have option #1 (using a base class) mapping to the current possibilities with the help of option #2, so we'll end up with something like that:

    public abstract class FooAttribute : Attribute {
        public FooAttribute(FooType type) {}
    }

    public class AaaaFooAttribute: FooAttribute {
       public AaaaFooAttribute : base(FooType.Aaaa){
       }
    }
    public class BbbbFooAttribute: FooAttribute {
       public BbbbFooAttribute: base(FooType.Bbbb){
       }
    }
    public class CcccFooAttribute: FooAttribute {
       public CcccFooAttribute: base(FooType.Cccc){
       }
    }
    public class DdddFooAttribute: FooAttribute {
       public DdddFooAttribute: base(FooType.Dddd){
       }
    }

    public class MyFooAttribute: FooAttribute {
        public MyFooAttribute() : base(FooType.Custom){
    }
    }


    public enum FooType { Aaaa, Bbbb, Cccc, Dddd, Custom }
  • Anonymous
    August 28, 2006
    I prefer Option 1.

    Even though option 1 seems pretty clumsy to use. It makes life easier for future changes that might be required and not having to rebuild and redeploy for additional capabilities.

    -Vish
  • Anonymous
    August 29, 2006
    It depends :-)
    #1 may be easier when using reflection to search for Types or members. And that's one of the main scenarios for Attibutes.
  • Anonymous
    October 10, 2006
    I would prefer #1 specifically for the performance implications. Using reflection it is possible to identify if the member is targeted with the custom attribute without expecily creating it however with #2 you need to explicity create the attribute to identify its nature and intent. Saving the developer time is only one of the needs of the SDK design but it also must perform well and clearly state intent. I understand why some would consider #2 better for removing some of the white noise but clearly when evolving the api it seems to be a little more rigid.
  • Anonymous
    October 23, 2006
    Hi, in simple cases (as in your example) it really doesn't matter and comes down to "personal style" (which is what you asked for, right?). However in more complex scenarios several attribute types will be the way to go as they
  • support different attribute usages
  • can enforce different parametrization rules (such as dependencies between parameters) with respectively designed c'tors
  • can provide different default values, constants, etc.
  • upcoming changes can be applied more locally With this assessment in mind I come back to the initial statement and would favour the "several attribute types" approach for consistency. Alexander
  • Anonymous
    February 26, 2007
    For those that favor option 2 - what about the tradeoff of design-time vs run-time checking?  With option1, you will have compile-time validation that the option you've selected is legit, but with option 2 you could get the following past the compiler: [ FooAttribute( (FooType)int.MinValue ) ] I thought that having compile-time type checking was a major influence in choosing between design options...?

  • Anonymous
    June 05, 2008
    I would like to run a quick poll. Which design do you prefer for a set of related attributes and why? Option #1: Several Attribute Types: public class AaaaFooAttribute: Attribute { } public class BbbbFooAttribute: Attribute { } public class CcccFooAttribute