.net framework 2.0 introduced several great features which made a life easier for us developers a lot. One such feature is generics. The feature is closer to C++ templates but it has runtime type substitution compared to C++ templates which takes place at compile time. There are other differences too. This post is not about the introduction of the feature but this is about a limitation of the feature and possible workaround. The greatest read about the feature introduction can be found here:
http://msdn.microsoft.com/en-us/library/512aeb7t
Let's introduce an type which can be used to guard the parameters of a method. We will be adding specialized methods which can be used to verify if the argument fulfills certain parameter constraint. If not, generally an exception is thrown. We will be adding the flexibility to provide additional methods where we can just check the value rather than causing an exception.
We can use this Guard type as follows:
The developers who go back and forth between java and C# miss / appreciate the missing feature of throws for C# method. They miss them because there is no compile time support to check whether a possible exception is handled gracefully. They appreciate it because there are no handcuffs to handle exception which they don't want to. It also results in better versionability and scalability. Anders Hejlsberg has discussed in detail in this interview why they decided not to provide this feature in C#.
http://www.artima.com/intv/handcuffs.html
Now we come back to the topic and discuss what the above code does. As discussed it provides to methods. The actual verification code is provided by the client code because they better know what and how to verify. We just need to execute the code they provide. The constraint about verification code is that it must return a boolean. If verification fails then an exception is generated. The type of the exception would be the based on the type argument used with ArgumentGuard.
We are definitely the happiest developers in the world because we are able to provide an implementation which is simple and generalized. The client code not only would specify what and how to verify but it would also specify what exception should be resulted if the check in their own code fails. It's just like going to subway and choosing your options. This is exactly what they say, Tell me how do you want your sandwich. The only thing is that they don't let us make it ourselves and I don't forgive them for this mean attitude :).
The problem happens when one of the client developer shows up at your desk and requires the support of a particular exception message. That shouldn't be a big deal, we can just use the ArgumentException constructor which supports a message parameter. This might be the first thought which might come to your mind.
Well I have a bad news if you haven't got the issue with the above code. It seems that the compile doesn't like this and pukes this on us.
MSDN has details about the list of constraints possible with Generic Types [http://msdn.microsoft.com/en-us/library/d5x73970%28v=vs.100%29]. So you wouldn't be able to create an exception instance by using one of the constructor of ArgumentException which supports the parameter. The developer mindset puts us to bypass this limitation by this approach:
This approach works most of the time as we are bypassing the type argument constructor constraint by using the legal parameterless constructor. The type argument's type constraint lets us sets the property and doesn't seem to mind. But this is also not possible in this case as the Message property is read-only...Damn!!!
Am I still alive? A non-dead person always has some options in even the most desperate situation. Since we seem to be stuck here, we might ask this question to ourselves. But actually we haven't run out of options yet. One option is to get rid of the idea of using generics at all for this feature. Why should we use a language feature which doesn't support something that we actually need. Since there are only a few child classes of ArgumentException. This shouldn't be a big deal. We can make client's life easier by naming them as such that these types appear together in the intellisence so that client can make better decision about which ArgumentGuard he needs to use.
An evil developer still has some arrows to shoot. Let me say this and then we will use it, "We can set the value of a read-only field through reflection". Feels light once you let it out, right? So what if we set the Message using reflection, it should be fine. Just sneak it by avoiding the crappy purists like myself and you should be fine :(
Thanks God that it wouldn't work. Stupid myself, Message is not a field rather it is a property with no setter at all. It's not like that there is a setter but it is private. There is none whatsoever. We can use dotPeek to peek the definition of the type. It is overriding the Message property from its parent type but still providing no setter. Had there been a private setter, it would have worked like a charm.
So we can't set Message because it just has getter and no setter. Can we use the backing field for this property? In the base class in the hierarchy, eventually it is backed by _message field in System.Exception. On the way, to Argument's children, Message is overridden a few times but if we don't change anything else then the code below should work most of the times.
Since _message is an internal field in mscorlib assembly, we needed to use relevant BindingFlags to access the field. Now we can update the client code as follows:
In the above code, the verification criteria demands that a must not be null. Since the type argument is ArgumentNullException, it is expecting an ArgumentNullException with the appropriate message set. Let's try calling the method with null argument and test if it works as expected.
Now when we run it we do get the appropriate exception with the exact message as expected.
Hmm, this is working as expected. We are able to use generics for argument guard. We are also able to provide the expected exception message. But can you really call it a good design. Poking our noses into others private and internal members is never a recommended approach. The API owner is free to change the internal implementation as much and as often. As long as they are not changing the public behavior of types, they are not the one to blame if something in our code breaks just because they changed their internal implementation.
But is there really a way out? Aren't we stuck? We can't set the Message property (no setter constraint) and we can not use parameterized constructor (generics constructor constraint). It's time to take a step back and see do we really need to instantiate the Exception in Verify method. We just need to make sure that the exception is generated with appropriate message. What if we turn the table around and ask the caller to provide us the exception instantiation code. We will be generating the exception as provided to us by the caller himself. We don't need to set any property because it is now caller's responsibility to provide us with appropriate object with all properties set. If required, we will be executing the code provided by caller. There are no memory issues as the object is constructed only when required. This code would provide us the relevant exception object and we will be throwing that exception. Here we can constraint the type of exception based on the same type argument. Let's provide an overload of the same Verify method.
Now we can use the above guard argument helper as follows:
Now let's run the code again and see how it works.
Zindabad!!!
Download Code
Sunday, May 27, 2012
C# Generics Constructor Constraint & Guarding Arguments
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment