I have a number of types for which I need to provide custom functions that talk to the external world. For example, I may have a Widget, and a Sprocket, and when data from the world that I don't control arrives and says "make a Widget," then I need to call a Widget.Create() function. If the world says "make a Hammer," then I need to return a "Hammer does not exist" error. However, the mapping between world-representation and type-name is not 1:1, so I can't simply use reflection to find the type name directly -- I need a table. (In fact, "name" may for example be a specific integer value.)
I understand how I can use a pre-defined table (or Dictionary) that maps from world-representation to class-name. I also understand how to extend/change this table at runtime if the set of possible classes changes. (Because of rules changes, or dynamically loaded parts of the application, or whatever.)
However, all of that requires duplication -- I have to both implement my class, and then, in some other place in the code, remember to add an instance of "this class has this name in the external world." This is a bit of a code smell, because sometime in the future I will write a new class (or delete an old class) and forget to update the table, and then spend time debugging why it doesn't work right.
I thought I could use a static member in each class which registers itself in a global table:
public static Registration Me = new Registration(typeid(MyClass), "My Name");
Unfortunately, static fields are not initialized until some function in the class is executed/accessed, so this doesn't run at start-up. (Static constructors have similar limitations, plus even more overhead in the runtime!)
The next idea was to decorate the class with a custom attribute that says "register this class in the table."
[Register("My Name")]
class MyClass : .... {
}
Unfortunately, the "Register" attribute doesn't know what class it is attached to -- only the MyClass class knows that it has the Register attribute. (This is infuriating to me, as it would be SO CONVENIENT if attributes knew where they were attached, in many, many cases. But that's an aside!)
So, the least bad implementation I can think of is to iterate all the types of all the assemblies, using reflection, and check whether they have this attribute, and if they do, register them in the table. This is, shall we say, neither elegant nor efficient.
Is there a better way to auto-register classes without having to update some other source file with a central registry?
Aucun commentaire:
Enregistrer un commentaire