|
dev
newsgroups
|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
Using InvokeMethod to circumvent compile-time type checkingabstract class. I have a separate comparer class full of AreEqual() methods, each with a signature that accepts a pair of concrete classes: AreEqual(ConcreteA x, ConcreteA y) AreEqual(ConcreteB x, ConcreteB y) etc. I've got another method designed to compare arrays of concrete types. I'd like to have a single method, AreEqual(Abstract[] x, Abstract[] y) that calls the appropriate AreEqual() function above for all permutations of the elements in the arrays. Of course, the compiler wouldn't let me do this at compile-time, 'cause even though the arrays are full of concrete instances, it can't perform the (widening?) conversion from Abstract to ConcreteA or ConcreteB or whatever the type may be. So I started investigating reflection, and came up with this solution: I created an additional AreEqual() method, public bool AreEqual(Abstract x, Abstract y) { Type t = this.GetType(); return (bool)t.InvokeMember("AreEqual", BindingFlags.InvokeMethod, null, this, new Object[] {x, y}); } And it works fine! I use that method in my array comparison and it seems to work like a charm. But I'm worried that this was too easy, and that I'm missing something. (The BindingFlags don't all seem to be very clear.) Should it really be so trivial to bypass compile-time type checking? Am I "cheating" doing this? -- Jeff S. Abstract classes can serve you well, but accepting an instance of an abstract
class merely to get around a contract is not a wise move (if this is your motivation). This is the biggest danger I can see. Another potential issue here is having to call methods that are outside of the abstract class (derived class specific methods). In these cases, you end up booting polymorphism and end up with a tightly coupled implementation that has the false appearance of being loosely coupled. Rather than deleting dependencies, you are obfuscating them Those are two potential gotchas I can see. -- Show quoteGregory A. Beamer MVP; MCP: +I, SE, SD, DBA *************************** Think Outside the Box! *************************** "Jeff Stewart" wrote: > I've got several classes that are all concrete implementations of an > abstract class. I have a separate comparer class full of AreEqual() > methods, each with a signature that accepts a pair of concrete classes: > > AreEqual(ConcreteA x, ConcreteA y) > AreEqual(ConcreteB x, ConcreteB y) > etc. > > I've got another method designed to compare arrays of concrete types. > I'd like to have a single method, > > AreEqual(Abstract[] x, Abstract[] y) > > that calls the appropriate AreEqual() function above for all > permutations of the elements in the arrays. > > Of course, the compiler wouldn't let me do this at compile-time, 'cause > even though the arrays are full of concrete instances, it can't perform > the (widening?) conversion from Abstract to ConcreteA or ConcreteB or > whatever the type may be. > > So I started investigating reflection, and came up with this solution: > I created an additional AreEqual() method, > > public bool AreEqual(Abstract x, Abstract y) { > Type t = this.GetType(); > return (bool)t.InvokeMember("AreEqual", BindingFlags.InvokeMethod, > null, this, new Object[] {x, y}); > } > > And it works fine! I use that method in my array comparison and it > seems to work like a charm. But I'm worried that this was too easy, > and that I'm missing something. (The BindingFlags don't all seem to be > very clear.) Should it really be so trivial to bypass compile-time > type checking? Am I "cheating" doing this? > > -- > Jeff S. > > My motivation is two-fold: 1) don't implement a separate AreEqual()
overload for every possible combination of -arrays- of elements, because the array comparison algorithm is common to all scenarios, and 2) facilitate extensibility by allowing inherited comparer classes to define their own AreEqual() methods for any concrete classes that may be invented in the future. Again, the array comparison algorithm has only one foreseeable implementation, so it seems natural to want to reuse it as much as possible. I'm not clear on your second point about obfuscating dependencies. Though I don't foresee using this technique on derived-class-specific methods in this project, if I decided to in another, how does this tight coupling you're talking about manifest itself? To me, calling this InvokeMember mechanism seems very flexible. (Indeed, it seemed so flexible that I worried it might be too flimsy.) -- Jeff S. |
|||||||||||||||||||||||