:
// Some calls which are logically properties end up
// as methods…
IList<Foo> foos = Enums.GetValues<Foo>();
// Type infererence for extenion methods
// Note that we couldn’t have a Description property
// as we don’t have extension properties
string firstDescription = foos[0].GetDescription();
// Option 2: Use just a generic type:
// Now we can use a property…
IList<Foo> foos = Enums<Foo>.Values;
// But we can’t use type inference
string firstDescription = Enums<Foo>.GetDescription(foos[0]);
// Option 3: Use a mixture (Enums and Enums<T>):
IList<Foo> foos = Enums<Foo>.Values;
// All looks good…
string firstDescription = foos[0].GetDescription();
// … but the user has to know when to use which class.)
10 thoughts on “API design: choosing between non-ideal options”
What criterion are you using to determine that GetValues() should be a property?
I don’t see a problem with it being a method. It wouldn’t even have occurred to me to make it a property.
As a user of the API, I’d prefer Option 1 for the simplicity and explorability. Even though Description should be a property, we all know how to use a Get/Set method pair.
You might want to make a note in the doc’s remarks explaining why it was done that way, though. :)
According to FxCop rule CA1024, methods must not be changed into properties if they return an array.
@wcoenan: It doesn’t return an array though. There’s a separate method which explicitly does that. It returns an immutable list – and so it’s returning the same value on every occurrence. That’s why it would be reasonable to be a property. The XML documentation explicitly says that the list returned is immutable.
(I’ve considered renaming GetValues() to GetValuesList() to avoid confusion with the existing method in Enum…)
Jon
System.Linq.Enumerable has a similar issue with its Empty method. I do forget the parens sometimes, but I would still choose option 1 over sacrificing inference or splitting the class.
Consider how it reads out loud:
“Enums, get values of Foo.”
or,
“Enums of Foo, get values.”
I think putting only the extension methods into the non-generic class would still be discoverable enough. It is a bit of an arbitrary split, but I’d prefer the consuming code to look as clean and logical as possible (though not at the expense of discoverability, but as previously mentioned, I don’t think discoverability would be harmed too much).
In a library I would give precedence to use over creation always…
Since discoverability for the extension methods does not require the name, only the namespace, making it a separate class makes sense.
In truth I would think that few people would use the properties via fully qualified names so simply importing the main namespace gives you IDE discoverability for the extensions and puts the Enums class there in easy reach.
I think I would prefer option 1, because it takes advantage of generic type inference.
The only reason for preferring properties would be for data binding scenarios (properties can be bound to, methods can’t). However I can’t think of a useful binding scenarios involving UnconstrainedMelody, so that reason is probably not enough to tip the scales…
I would vote for option 1. Making Values into a property is not particularly compelling for me; I understand the argument from a strict component-oriented-design perspective, but in this case I think GetValues() is just as natural (although I admit that may be because I am used to Enum.GetValues).
Nice podcast on the Connected Show.