OK, we are migrating to .NET 4.5. I was long waiting the moment working with .NET Code Contracts.
First, I must mention historical reasons for this.
Several years ago, I stumbled on these blog posts: 1, 2, 3, 4
It opened my eyes. Before that, I really wasn't sure what means "Static Analysis", what "Strong Typed" language means. For instance, when I started working with C#, I started working with some standard data-centric application. Before that I worked with VB 6 and I used recordsets to retrieve data from Database. There, everything was loosely typed. So, when I continued working in C# with new API for retrieving data from database, I learned how to work with DataSets, but actually I didn't learned as I should, because I used them almost in the same way as I used recordsets. How is that possible?
Well, in the beginning, when I started learning new API, everyone talked about "typed" and "untyped" datasets. And of course, I didn't know the difference, but I knew what suits better to me at the time - it was "untyped" datasetes. OK, but what is the difference? Well, here is a small example:
You could write:
DataSet ds;
ds.Tables["MyTable"];
Or you could write if you worked with typed datastet:
MyDataSet ds;
ds.MyTable;
It seems rather small difference, but yet, it is huge. So, as I said, in the beginning I used untyped datasets, I even felt bad when I tried using typed datasets. It all seemed to me that this thing is falling a part (you know, when I change something, my build is falling). I even "complained" about that on popular Serbian forum.
So, years have passed and I started to realize that having some compile checks, in particular errors, before I even start my application is maybe not a bad thing. It even started to be more interesting when I stumbled on the links from above. I started to think about code analysis, about type system and how can I push it to its limits.
So, lets write similar example to the one in links above:
Lets say that we have function that needs to extract extension from given file name. We could write something as following:
public string CutExtension(string text)
{
string output = text.Substring(text.Length - 3);
return output;
}
OK, this looks good, but what if we pass null value?
CutExtension(null);
We'll get NullReferenceException.
So, we can make this code a bit safer:
public string CutExtension(string text)
{
if (text == null)
{
return null;
}
string output = text.Substring(text.Length - 3);
return output;
}
Now we have more robust code. But is it? How can the caller know that you will return null in case that text is null? Yes, he can test the function, but what if you have yet another "function domain" that he must infer? OK, you can write documentation, which in most cases will nobody read, until they get exception.
But is there anything else we can do? Well, there is, and it is proposed in links above:
Now, lets modify this function a little bit.
public string CutExtension(string! text)
{
string output = text.Substring(text.Length - 3);
return out
}
OK, but this is almost the same code as first example, except that there is some illegal exclamation mark next to string type parameter definition - except it is not illegal in our hypothetical C# language. What if - if you could add exclamation mark next to the type and compiler will treat that type as it cannot be null. So, if you try to pass null value, it will generate an error.
But, how can it be?
What if you try to do something like this:
string fakeFile = null;
string fileName = "test.cml";
fileName = fakeFile ;
CutExtension(fakeFile);
Will it fool the compiler? Well... no.
Because, compiler expected you to pass string! type, not string type. This is more specialized type of string, so you cannot pass the string type to function. It is almost the same as string, but it is not the same.
But now, someone could say "you need to handle null value somewhere". What if you had API which is passing to you some file name which can be null. For instance:
string fileName = NetworkDispatcher.GetFileName();
CutExtension(fileName);
Again, you are passing string to expected string! type, which is illegal. So, if you want to pass somehow non null string you must convert it, or compiler must be smart enough to see that you tested whether string is null or not.
First example:
string fileName = NetworkDispatcher.GetFileName();
string! nonNullFileName = ConvertToNonNull(fileName);
CutExtension(fileName);
Second example:
string fileName = NetworkDispatcher.GetFileName();
if (fileName != null)
{
CutExtension(fileName);
}
else
{
// DoSomething
}
First example is a bit obscure. What happens if fileName is null? Well, the only reasonable solution is to throw an exception. So, we must know that, so we can handle that (but yes, this is another story, exception vs return value). For now, lets keep with second example.
In second example we are forcing the caller to handle situation when he has a null value. Our function is telling loudly that it doesn't accept null value, so why even allowing to pass null in the first place. It's a caller's job, not ours. But, someone could say "but what is the difference, whether you handle here or there" - but it is a big difference. Consider this code:
public void ProcessLayerA(string fileName)
{
ProcessLayerB(fileName);
}
public void ProcessLayerB(string fileName)
{
CutExtension(fileName);
}
public string CutExtension(string text)
{
string output = text.Substring(text.Length - 3);
return output;
}
And call to ProcessLayerA:
string fileName = NetworkDispatcher.GetFileName();
ProcessLayerA(fileName);
So, without exclamation mark (!) constraint, we should consider where to handle not null situation. We can write for not-null check in ProcessLayerA function, or ProcessLayerB function, or CutExtension function itself, or we can write on all levels, which is of course, redundant and still we don't have an explicit "rule" that will say to us that file name must not be null.
OK, we can see now hypothetical usefulness of exclamation mark. But that is just a null value. In our function, there are also some other issues. For instance, what if file name is less than 3 chars? It will fail. But what if if we had in our hypothetical C# ability to mark string type in that way so we can constraint how long string should be?
Lets say that we have sign $ for which we specify length of expected string:
public string CutExtension(string!$3 text)
{
string output = text.Substring(text.Length - 3);
return output;
}
And same as for null type constraint, we add this one too next to string type declaration. So, if someone tries to pass text parameter as only string type, or string! type, it is still not valid for us. We except more specialized string type even more than string! type.
So, as we can see, we can do all variety of things here, except it would be crowdy to specialize type in this way. We could use some different approach.... so lets welcome Code Contracts :)
Here is the same thing written with contracts:
public string CutExtension(string text)
{
Contract.Requires(text != null);
Contract.Requires(text.Length > 3);
string output = text.Substring(text.Length - 3);
return output;
}
And that's all for this part. In next part I will try to cover some Code Contracts usage and some workarounds, just to explain how Contracts are really just simple compiler extensions.
First, I must mention historical reasons for this.
Several years ago, I stumbled on these blog posts: 1, 2, 3, 4
It opened my eyes. Before that, I really wasn't sure what means "Static Analysis", what "Strong Typed" language means. For instance, when I started working with C#, I started working with some standard data-centric application. Before that I worked with VB 6 and I used recordsets to retrieve data from Database. There, everything was loosely typed. So, when I continued working in C# with new API for retrieving data from database, I learned how to work with DataSets, but actually I didn't learned as I should, because I used them almost in the same way as I used recordsets. How is that possible?
Well, in the beginning, when I started learning new API, everyone talked about "typed" and "untyped" datasets. And of course, I didn't know the difference, but I knew what suits better to me at the time - it was "untyped" datasetes. OK, but what is the difference? Well, here is a small example:
You could write:
DataSet ds;
ds.Tables["MyTable"];
Or you could write if you worked with typed datastet:
MyDataSet ds;
ds.MyTable;
It seems rather small difference, but yet, it is huge. So, as I said, in the beginning I used untyped datasets, I even felt bad when I tried using typed datasets. It all seemed to me that this thing is falling a part (you know, when I change something, my build is falling). I even "complained" about that on popular Serbian forum.
So, years have passed and I started to realize that having some compile checks, in particular errors, before I even start my application is maybe not a bad thing. It even started to be more interesting when I stumbled on the links from above. I started to think about code analysis, about type system and how can I push it to its limits.
So, lets write similar example to the one in links above:
Lets say that we have function that needs to extract extension from given file name. We could write something as following:
public string CutExtension(string text)
{
string output = text.Substring(text.Length - 3);
return output;
}
OK, this looks good, but what if we pass null value?
CutExtension(null);
We'll get NullReferenceException.
So, we can make this code a bit safer:
public string CutExtension(string text)
{
if (text == null)
{
return null;
}
string output = text.Substring(text.Length - 3);
return output;
}
Now we have more robust code. But is it? How can the caller know that you will return null in case that text is null? Yes, he can test the function, but what if you have yet another "function domain" that he must infer? OK, you can write documentation, which in most cases will nobody read, until they get exception.
But is there anything else we can do? Well, there is, and it is proposed in links above:
Now, lets modify this function a little bit.
public string CutExtension(string! text)
{
string output = text.Substring(text.Length - 3);
return out
}
OK, but this is almost the same code as first example, except that there is some illegal exclamation mark next to string type parameter definition - except it is not illegal in our hypothetical C# language. What if - if you could add exclamation mark next to the type and compiler will treat that type as it cannot be null. So, if you try to pass null value, it will generate an error.
But, how can it be?
What if you try to do something like this:
string fakeFile = null;
string fileName = "test.cml";
fileName = fakeFile ;
CutExtension(fakeFile);
Will it fool the compiler? Well... no.
Because, compiler expected you to pass string! type, not string type. This is more specialized type of string, so you cannot pass the string type to function. It is almost the same as string, but it is not the same.
But now, someone could say "you need to handle null value somewhere". What if you had API which is passing to you some file name which can be null. For instance:
string fileName = NetworkDispatcher.GetFileName();
CutExtension(fileName);
Again, you are passing string to expected string! type, which is illegal. So, if you want to pass somehow non null string you must convert it, or compiler must be smart enough to see that you tested whether string is null or not.
First example:
string fileName = NetworkDispatcher.GetFileName();
string! nonNullFileName = ConvertToNonNull(fileName);
CutExtension(fileName);
Second example:
string fileName = NetworkDispatcher.GetFileName();
if (fileName != null)
{
CutExtension(fileName);
}
else
{
// DoSomething
}
First example is a bit obscure. What happens if fileName is null? Well, the only reasonable solution is to throw an exception. So, we must know that, so we can handle that (but yes, this is another story, exception vs return value). For now, lets keep with second example.
In second example we are forcing the caller to handle situation when he has a null value. Our function is telling loudly that it doesn't accept null value, so why even allowing to pass null in the first place. It's a caller's job, not ours. But, someone could say "but what is the difference, whether you handle here or there" - but it is a big difference. Consider this code:
public void ProcessLayerA(string fileName)
{
ProcessLayerB(fileName);
}
public void ProcessLayerB(string fileName)
{
CutExtension(fileName);
}
public string CutExtension(string text)
{
string output = text.Substring(text.Length - 3);
return output;
}
And call to ProcessLayerA:
string fileName = NetworkDispatcher.GetFileName();
ProcessLayerA(fileName);
So, without exclamation mark (!) constraint, we should consider where to handle not null situation. We can write for not-null check in ProcessLayerA function, or ProcessLayerB function, or CutExtension function itself, or we can write on all levels, which is of course, redundant and still we don't have an explicit "rule" that will say to us that file name must not be null.
OK, we can see now hypothetical usefulness of exclamation mark. But that is just a null value. In our function, there are also some other issues. For instance, what if file name is less than 3 chars? It will fail. But what if if we had in our hypothetical C# ability to mark string type in that way so we can constraint how long string should be?
Lets say that we have sign $ for which we specify length of expected string:
public string CutExtension(string!$3 text)
{
string output = text.Substring(text.Length - 3);
return output;
}
And same as for null type constraint, we add this one too next to string type declaration. So, if someone tries to pass text parameter as only string type, or string! type, it is still not valid for us. We except more specialized string type even more than string! type.
So, as we can see, we can do all variety of things here, except it would be crowdy to specialize type in this way. We could use some different approach.... so lets welcome Code Contracts :)
Here is the same thing written with contracts:
public string CutExtension(string text)
{
Contract.Requires(text != null);
Contract.Requires(text.Length > 3);
string output = text.Substring(text.Length - 3);
return output;
}
And that's all for this part. In next part I will try to cover some Code Contracts usage and some workarounds, just to explain how Contracts are really just simple compiler extensions.
No comments:
Post a Comment