Mono + named/optional parameters = compiler bug? - c#

I have encountered some unforeseen consequences of porting to mono 2.8.1. Problem can be boiled down to a sample program (I have been unable to reduce it further, after cutting several classes and ~1000 lines of code to the file quoted below)
public class Person
{
public Person(int age, string name = null){}
public Person(double income, string name = null){}
public Person(double income, int age, string name = null){}
}
class Program
{
static void Main()
{
Person p = new Person(1.0, name: "John Doe");
}
}
Compilation of above code with mcs gives output:
test.cs(22,24): error CS0584: Internal compiler error: Internal error
test.cs(22,20): error CS0266: Cannot implicitly convert type `object' to `NamedParams.Person'.
An explicit conversion exists (are you missing a cast?)
Compilation failed: 2 error(s), 0 warnings
Removing use of optional/named parameter (i.e. calling new Person(1.0, null, "John Doe") or new Person(1.0, null, name:"John Doe"), or new Person(1.0, "John Doe") ) leads to flawless compilation. Also, under VS2010 the file (and whole solution with which I started with) compiles fine. Casting removes error CS0266, but not CS0584 -- so no surprise there.
My question: is it me doing something wrong, or mcs (i.e. bug in mcs is obvious to me -- what else ,,internal error'' would mean, but perhaps it's ok such program won't compile), or maybe Microsoft compiler in VS2010 should not let such code to compile?
I bet it's mcs who's wrong (unable to guess right constructor), but perhaps it's otherwise and I shouldn't know better?
PS. I tried searching for a known bug like this in both Google and Novell's Bugzilla, but was unable to find anything relevant. Again, I may be blind ;)

Okay, here goes. The crash is indeed due to the third overload, Person(double income, int age, string name = null).
The compiler sees you are trying to pass less arguments than what are listed in the signature so it goes looking for optional arguments. It happily notices that name is optional and assumes you are not passing that argument. It does this by appending a placeholder at the end of the supplied arguments. Next, it goes to reorder the named arguments in the list so they end up in their right position. This means John Doe is now correctly at the last position for name, but the placeholder gets into the age position. The compiler then tries to fill in the default values, but is shocked to find a placeholder at a location which has no default value. It thinks this can not happen, since the placeholder was only added for an optional argument and now suddenly it is not optional anymore. Not knowing what to do, it throws an exception.
The following patch seems to fix the issue (however it may break something else, so no warranty):
--- mono-2.6.7.orig/mcs/mcs/ecore.cs 2009-10-02 12:51:12.000000000 +0200
+++ mono-2.6.7/mcs/mcs/ecore.cs 2010-12-21 02:26:44.000000000 +0100
## -3803,6 +3803,15 ##
int args_gap = Math.Abs (arg_count - param_count);
if (optional_count != 0) {
+ // readjust for optional arguments passed as named arguments
+ for (int i = 0; i < arguments.Count; i++) {
+ NamedArgument na = arguments[i] as NamedArgument;
+ if (na == null)
+ continue;
+ int index = pd.GetParameterIndexByName (na.Name.Value);
+ if (pd.FixedParameters[index].HasDefaultValue)
+ optional_count--;
+ }
if (args_gap > optional_count)
return int.MaxValue - 10000 + args_gap - optional_count;

For others coming to this thread. The bug is still there in the latest versions of Mono Compiler as of April 2013. I created a work around without needing to modify the compiler using C# overloaded functions.
Foo(int a, bool b = true) {
Foo(a, b, "Default String");
}
Foo(int a, bool b, string c)

Related

What does _= mean in C#? [duplicate]

While going through new C# 7.0 features, I stuck up with discard feature. It says:
Discards are local variables which you can assign but cannot read
from. i.e. they are “write-only” local variables.
and, then, an example follows:
if (bool.TryParse("TRUE", out bool _))
What is real use case when this will be beneficial? I mean what if I would have defined it in normal way, say:
if (bool.TryParse("TRUE", out bool isOK))
The discards are basically a way to intentionally ignore local variables which are irrelevant for the purposes of the code being produced. It's like when you call a method that returns a value but, since you are interested only in the underlying operations it performs, you don't assign its output to a local variable defined in the caller method, for example:
public static void Main(string[] args)
{
// I want to modify the records but I'm not interested
// in knowing how many of them have been modified.
ModifyRecords();
}
public static Int32 ModifyRecords()
{
Int32 affectedRecords = 0;
for (Int32 i = 0; i < s_Records.Count; ++i)
{
Record r = s_Records[i];
if (String.IsNullOrWhiteSpace(r.Name))
{
r.Name = "Default Name";
++affectedRecords;
}
}
return affectedRecords;
}
Actually, I would call it a cosmetic feature... in the sense that it's a design time feature (the computations concerning the discarded variables are performed anyway) that helps keeping the code clear, readable and easy to maintain.
I find the example shown in the link you provided kinda misleading. If I try to parse a String as a Boolean, chances are I want to use the parsed value somewhere in my code. Otherwise I would just try to see if the String corresponds to the text representation of a Boolean (a regular expression, for example... even a simple if statement could do the job if casing is properly handled). I'm far from saying that this never happens or that it's a bad practice, I'm just saying it's not the most common coding pattern you may need to produce.
The example provided in this article, on the opposite, really shows the full potential of this feature:
public static void Main()
{
var (_, _, _, pop1, _, pop2) = QueryCityDataForYears("New York City", 1960, 2010);
Console.WriteLine($"Population change, 1960 to 2010: {pop2 - pop1:N0}");
}
private static (string, double, int, int, int, int) QueryCityDataForYears(string name, int year1, int year2)
{
int population1 = 0, population2 = 0;
double area = 0;
if (name == "New York City")
{
area = 468.48;
if (year1 == 1960) {
population1 = 7781984;
}
if (year2 == 2010) {
population2 = 8175133;
}
return (name, area, year1, population1, year2, population2);
}
return ("", 0, 0, 0, 0, 0);
}
From what I can see reading the above code, it seems that the discards have a higher sinergy with other paradigms introduced in the most recent versions of C# like tuples deconstruction.
For Matlab programmers, discards are far from being a new concept because the programming language implements them since very, very, very long time (probably since the beginning, but I can't say for sure). The official documentation describes them as follows (link here):
Request all three possible outputs from the fileparts function:
helpFile = which('help');
[helpPath,name,ext] = fileparts('C:\Path\data.txt');
The current workspace now contains three variables from fileparts: helpPath, name, and ext. In this case, the variables are small. However, some functions return results that use much more memory. If you do not need those variables, they waste space on your system.
Ignore the first output using a tilde (~):
[~,name,ext] = fileparts(helpFile);
The only difference is that, in Matlab, inner computations for discarded outputs are normally skipped because output arguments are flexible and you can know how many and which one of them have been requested by the caller.
I have seen discards used mainly against methods which return Task<T> but you don't want to await the output.
So in the example below, we don't want to await the output of SomeOtherMethod() so we could do something like this:
//myClass.cs
public async Task<bool> Example() => await SomeOtherMethod()
// example.cs
Example();
Except this will generate the following warning:
CS4014 Because this call is not awaited, execution of the
current method continues before the call is completed. Consider
applying the 'await' operator to the result of the call.
To mitigate this warning and essentially ensure the compiler that we know what we are doing, you can use a discard:
//myClass.cs
public async Task<bool> Example() => await SomeOtherMethod()
// example.cs
_ = Example();
No more warnings.
To add another use case to the above answers.
You can use a discard in conjunction with a null coalescing operator to do a nice one-line null check at the start of your functions:
_ = myParam ?? throw new MyException();
Many times I've done code along these lines:
TextBox.BackColor = int32.TryParse(TextBox.Text, out int32 _) ? Color.LightGreen : Color.Pink;
Note that this would be part of a larger collection of data, not a standalone thing. The idea is to provide immediate feedback on the validity of each field of the data they are entering.
I use light green and pink rather than the green and red one would expect--the latter colors are dark enough that the text becomes a bit hard to read and the meaning of the lighter versions is still totally obvious.
(In some cases I also have a Color.Yellow to flag something which is not valid but neither is it totally invalid. Say the parser will accept fractions and the field currently contains "2 1". That could be part of "2 1/2" so it's not garbage, but neither is it valid.)
Discard pattern can be used with a switch expression as well.
string result = shape switch
{
Rectangule r => $"Rectangule",
Circle c => $"Circle",
_ => "Unknown Shape"
};
For a list of patterns with discards refer to this article: Discards.
Consider this:
5 + 7;
This "statement" performs an evaluation but is not assigned to something. It will be immediately highlighted with the CS error code CS0201.
// Only assignment, call, increment, decrement, and new object expressions can be used as a statement
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/cs0201?f1url=%3FappId%3Droslyn%26k%3Dk(CS0201)
A discard variable used here will not change the fact that it is an unused expression, rather it will appear to the compiler, to you, and others reviewing your code that it was intentionally unused.
_ = 5 + 7; //acceptable
It can also be used in lambda expressions when having unused parameters:
builder.Services.AddSingleton<ICommandDispatcher>(_ => dispatcher);

IParameterSymbol.GetAttributes() does not return actual symbols

I'm writing the roslyn analyzer, that should check if method's parameter declared with [NotNull] attribute in current method declaration or in one of interfaces and makes some checks it method body. I registered the CodeBlockAction with RegisterCodeBlockAction, but when I trying to get attributes from parameter declaration in interfaces/base classes, that has it, sometimes it returns empty array.
I discovered, that is happens in case, if interface/base class is located in other assembly, and actualy analyzer works fine, when Intelisense runs it, but there is no warnings\errors in build output. I think this happens because of sematic analysis of referenced assembly is not completly finished (but it's a bit strange).
I wrote some logs
6/6/2019 13:59:47 Analize method symbol "ClassLibrary1.Program.Foo(string)" with 1 interfaces
6/6/2019 13:59:47 declaration ClassLibrary2.IFoo.Foo(string): [0 attributes] string s
6/6/2019 13:59:47 declaration ClassLibrary1.Program.Foo(string): [0 attributes] string s
6/6/2019 13:59:59 Analize method symbol "ClassLibrary1.Program.Foo(string)" with 1 interfaces
6/6/2019 13:59:59 declaration ClassLibrary2.IFoo.Foo(string): [1 attributes] string s
6/6/2019 13:59:59 declaration ClassLibrary1.Program.Foo(string): [0 attributes] string s
So you can see, that at 13:59:47 (msbuild run) there are no attributes, but at 13:59:59 (I opened document in Visual studio) there is one attribute.
There is how I get the iterfaces and parameters:
var allMethodDeclarations = //some code using methodSymbol.ContainingType.Interfaces
for (var i = 0; i < methodSymbol.Parameters.Length; ++i)
{
var currentParameter = methodSymbol.Parameters[i];
//parameters can be renamed, the only way is to use the order
var hasNotNull = allMethodDeclarations
.Select(d => d.Parameters[i])
.SelectMany(p => p.GetAttributes())
.Any(a => a.AttributeClass.Name == nameof(NotNullAttribute));
if (hasNotNull)
{
//do something
}
}
Example code on which bug is reproduced:
In assembly 1
public interface IFoo
{
void Foo([NotNull] string s);
}
In assembly 2, that references assembly 1
public class Program : IFoo
{
public void Foo(string s)
{
}
}
Okay, I got it. It was because of conditional compilation attribute on NotNullAttribute from Jetbrains.Annotations, so the compiler shows data from the actual referenced symbols (where it skips [NotNull] because JETBRAINS_ANNOTATIONS was not defined), and Visual Studio provides the real attributes list from code, and conditional compilation is not considered this way.
It looks inconsistent, but I had to define JETBRAINS_ANNOTATIONS in my project to make analyzer work.

Marshalling "as string" parameter from C#

Ran into an interesting problem that I was wondering if anyone can help explain. I have tried searching all over the internet and can’t seem to find an answer to this problem. While we have a solution, others might run into the same problem, and since it was random, it is very hard to track down.
We have an application that randomly fails when the computer is attached to the customers corporate network (don’t have a lot of information on the network). When we brought the computer to our environment it worked fine. The failure is fairly random, but as best as I could figure out occurred during calls from C# to an unmanaged third party vendors DLL. One of the trace backs I do have (only a couple failures provided traces in the system event log) points to a failure in ntdll.dll in the RtlMoveMemory function during the marshalling of parameters in calls to the unmanaged DLL.
The call to the unmanaged function was:
ThirdParty.CxxxAutomationWrapper clientDLL = new ThirdParty.CxxxAutomationWrapper () ;
object Parameter1 ;
:
string theValue = "abcde" ;
Parameter1 = theValue ;
:
if ( Parameter1 is string )
{
int returnCode = clientDLL.function ( Parameter1 as string ) ;
}
The call periodically fails on the customers computer but always works when run within Visual Studio (2010). The value of Parameter1 is correctly set (never null).
Since I changed the call to:
String parameter1String = Parameter1.ToString() ;
int returnCode = clientDLL.function ( parameter1String ) ;
everything has worked fine. The value of Parameter1 is very controlled and is not null prior to this code being executed.
The interface being called (clientDLL.function) is defined as:
HRESULT function ( [in] BSTR parameter,
[out, retval] long *ret);
With my [limited?] understanding of the ‘as’ statement in c#, I don’t understand why the first version of this failed. Can anybody help explain why this fails when called with the parameter specified as “as string”?
Because obj as string and obj.ToString() are not the same. The first is an attempted cast (sort of like (string)obj, but which returns null upon failure), while the seconds is a method call returning a string representation of the object.
Practically you can get the string representation of any object regardless of whether it's compatible with string as type, but the cast will fail if they are not compatible.
Simple example:
object obj = new object(); // simple object
Console.WriteLine(obj.ToString()); // prints System.Object
Console.WriteLine((obj as string) == null); // True: obj is not a string!
Parameter1.ToString() will render any .NET object to a string (i.e. instance of System.String). Parameter1 as string will return null if Parameter1 is null or if it is not an instance of System.String. If it is a class that wraps a string rather than actually being one, then you will end up with a null.
Neither will work in every case of course. If you have an instance of System.String which is null, then Parameter1 as string would correctly return null, whereas Parameter1.ToString() would throw a NullReferenceException.
A safe approach would be: Convert.ToString(Parameter1).
Parameter1 as string could be thought as
string foo
try
{
foo = (string)Parameter1;
}
catch
{
foo = null;
}
where .ToString() is
string foo = Parameter1.ToString();
The fact that Parameter1 is not directly castable to a string is the reson for it failing.

Variable declarations following if statements

An issue came up on another forum and I knew how to fix it, but it revealed a feature of the compiler peculiar to me. The person was getting the error "Embedded statement cannot be a declaration or labeled statement" because they had a declaration of a variable following an if statement with no brackets. That was not their intent, but they had commented out the line of code immediately following the if statement, which made the variable declaration the de facto line of code to execute. Anyway, that's the background, which brings me to this.
The following code is illegal
if (true)
int i = 7;
However, if you wrap that in brackets, it's all legal.
if (true)
{
int i = 7;
}
Neither piece of code is useful. Yet the second one is OK. What specifically is the explanation for this behavior?
The C# language specification distinguishes between three types of statements (see chapter 8 for more details). In general you can have these statements:
labeled-statement - my guess that this is for the old-fashioned goto statement
declaration-statement - which would be a variable declaration
embedded-statement - which includes pretty much all the remaining statements
In the if statement the body has to be embedded-statement, which explains why the first version of the code doesn't work. Here is the syntax of if from the specification (section 8.7.1):
if ( boolean-expression ) embedded-statement
if ( boolean-expression ) embedded-statement else embedded-statement
A variable declaration is declaration-statement, so it cannot appear in the body. If you enclose the declaration in brackets, you'll get a statement block, which is an embedded-statement (and so it can appear in that position).
When you don't include the brackets, it executes the next line as if it were surrounded by brackets. Since it doesn't make much sense to declare a variable in that line (you wouldn't be able to use it ever), the C# compiler will not allow this to prevent you from accidentally doing it without knowing (which might introduce subtle bugs).
Here's part of Eric Lippert has to say about the C# compiler on this SO answer about name resolution:
...C# is not a "guess what the user
meant" language...the compiler by
design complains loudly if the best
match is something that doesn't work
All compilers will allow you to compile code that is useless or of exceedingly low use. There are simply too many ways that a developer can use the language to create constructs with no use. Having the compiler catch all of them is simply too much effort and typically not worth it.
The second case is called out directly in the C# language specification at the start of section 8.0
The example results in a compile-time error because an if statement requires an embedded-statement rather than a statement for its if branch. If this code were permitted, then the variable i would be declared, but it could never be used. Note, however, that by placing i’s declaration in a block, the example is valid.
Example Code
void F(bool b) {
if (b)
int i = 44;
}
Adding the closing and opening braces on the else part of the if helped me like i have done below as opposed to what i was doing before adding them;
Before: this caused the error:
protected void btnAdd_Click(object sender, EventArgs e)
{
if (btnAdd.Text == "ADD")
{
CATEGORY cat = new CATEGORY
{
NAME = tbxCategory.Text.Trim(),
TOTALSALEVALUE = tbxSaleValue.Text.Trim(),
PROFIT = tbxProfit.Text.Trim()
};
dm.AddCategory(cat, tbxCategory.Text.Trim());
}
else
// missing brackets - this was causing the error
var c = getCategory();
c.NAME = tbxCategory.Text.Trim();
c.TOTALSALEVALUE = tbxSaleValue.Text.Trim();
c.PROFIT = tbxProfit.Text.Trim();
dm.UpdateCategory(c);
btnSearchCat_Click(btnSearchCat, e);
}
After: Added brackets in the else branch
protected void btnAdd_Click(object sender, EventArgs e)
{
if (btnAdd.Text == "ADD")
{
CATEGORY cat = new CATEGORY
{
NAME = tbxCategory.Text.Trim(),
TOTALSALEVALUE = tbxSaleValue.Text.Trim(),
PROFIT = tbxProfit.Text.Trim()
};
dm.AddCategory(cat, tbxCategory.Text.Trim());
}
else
{
var c = getCategory();
c.NAME = tbxCategory.Text.Trim();
c.TOTALSALEVALUE = tbxSaleValue.Text.Trim();
c.PROFIT = tbxProfit.Text.Trim();
dm.UpdateCategory(c);
}
btnSearchCat_Click(btnSearchCat, e);
}

What is the smoothest, most appealing syntax you've found for asserting parameter correctness in c#?

A common problem in any language is to assert that parameters sent in to a method meet your requirements, and if they don't, to send nice, informative error messages. This kind of code gets repeated over and over, and we often try to create helpers for it. However, in C#, it seems those helpers are forced to deal with some duplication forced upon us by the language and compiler. To show what I mean, let me present some some raw code with no helpers, followed by one possible helper. Then, I'll point out the duplication in the helper and phrase my question precisely.
First, the code without any helpers:
public void SomeMethod(string firstName, string lastName, int age)
{
if(firstName == null)
{
throw new WhateverException("The value for firstName cannot be null.");
}
if(lastName == null)
{
throw new WhateverException("The value for lastName cannot be null.");
}
// Same kind of code for age, making sure it is a reasonable range (< 150, for example).
// You get the idea
}
}
Now, the code with a reasonable attempt at a helper:
public void SomeMethod(string firstName, string lastName, int age)
{
Helper.Validate( x=> x !=null, "firstName", firstName);
Helper.Validate( x=> x!= null, "lastName", lastName);
}
The main question is this: Notice how the code has to pass the value of the parameter and the name of the parameter ("firstName" and firstName). This is so the error message can say, "Blah blah blah the value for the firstName parameter." Have you found any way to get around this using reflection or anything else? Or a way to make it less painful?
And more generally, have you found any other ways to streamline this task of validating parameters while reducing code duplication?
EDIT: I've read people talking about making use of the Parameters property, but never quite found a way around the duplication. Anyone have luck with that?
Thanks!
You should check out Code Contracts; they do pretty much exactly what you're asking. Example:
[Pure]
public static double GetDistance(Point p1, Point p2)
{
CodeContract.RequiresAlways(p1 != null);
CodeContract.RequiresAlways(p2 != null);
// ...
}
Wow, I found something really interesting here. Chris above gave a link to another Stack Overflow question. One of the answers there pointed to a blog post which describes how to get code like this:
public static void Copy<T>(T[] dst, long dstOffset, T[] src, long srcOffset, long length)
{
Validate.Begin()
.IsNotNull(dst, “dst”)
.IsNotNull(src, “src”)
.Check()
.IsPositive(length)
.IsIndexInRange(dst, dstOffset, “dstOffset”)
.IsIndexInRange(dst, dstOffset + length, “dstOffset + length”)
.IsIndexInRange(src, srcOffset, “srcOffset”)
.IsIndexInRange(src, srcOffset + length, “srcOffset + length”)
.Check();
for (int di = dstOffset; di < dstOffset + length; ++di)
dst[di] = src[di - dstOffset + srcOffset];
}
I'm not convinced it is the best answer yet, but it certainly is interesting. Here's the blog post, from Rick Brewster.
This may be somewhat helpful:
Design by contract/C# 4.0/avoiding ArgumentNullException
I tackled this exact problem a few weeks ago, after thinking that it is strange how testing libraries seem to need a million different versions of Assert to make their messages descriptive.
Here's my solution.
Brief summary - given this bit of code:
int x = 3;
string t = "hi";
Assert(() => 5*x + (2 / t.Length) < 99);
My Assert function can print out the following summary of what is passed to it:
(((5 * x) + (2 / t.Length)) < 99) == True where
{
((5 * x) + (2 / t.Length)) == 16 where
{
(5 * x) == 15 where
{
x == 3
}
(2 / t.Length) == 1 where
{
t.Length == 2 where
{
t == "hi"
}
}
}
}
So all the identifier names and values, and the structure of the expression, could be included in the exception message, without you having to restate them in quoted strings.
Alright guys, it's me again, and I found something else that is astonishing and delightful. It is yet another blog post referred to from the other SO question that Chris, above, mentioned.
This guy's approach lets you write this:
public class WebServer
{
public void BootstrapServer( int port, string rootDirectory, string serverName )
{
Guard.IsNotNull( () => rootDirectory );
Guard.IsNotNull( () => serverName );
// Bootstrap the server
}
}
Note that there is no string containing "rootDirectory" and no string containing "serverName"!! And yet his error messages can say something like "The rootDirectory parameter must not be null."
This is exactly what I wanted and more than I hoped for. Here's the link to the guy's blog post.
And the implementation is pretty simple, as follows:
public static class Guard
{
public static void IsNotNull<T>(Expression<Func<T>> expr)
{
// expression value != default of T
if (!expr.Compile()().Equals(default(T)))
return;
var param = (MemberExpression) expr.Body;
throw new ArgumentNullException(param.Member.Name);
}
}
Note that this makes use of "static reflection", so in a tight loop or something, you might want to use Rick Brewster's approach above.
As soon as I post this I'm gonna vote up Chris, and the response to the other SO question. This is some good stuff!!!
Using my library The Helper Trinity:
public void SomeMethod(string firstName, string lastName, int age)
{
firstName.AssertNotNull("firstName");
lastName.AssertNotNull("lastName");
...
}
Also supports asserting that enumeration parameters are correct, collections and their contents are non-null, string parameters are non-empty etcetera. See the user documentation here for detailed examples.
Here's my answer to the problem. I call it "Guard Claws". It uses the IL parser from the Lokad Shared Libs but has a more straightforward approach to stating the actual guard clauses:
string test = null;
Claws.NotNull(() => test);
You can see more examples of it's usage in the specs.
Since it uses real lambdas as input and uses the IL Parser only to generate the exception in the case of a violation it should perform better on the "happy path" than the Expression based designs elsewhere in these answers.
The links are not working, here is the URL:
http://github.com/littlebits/guard_claws/
The Lokad Shared Libraries also have an IL parsing based implementation of this which avoids having to duplicate the parameter name in a string.
For example:
Enforce.Arguments(() => controller, () => viewManager,() => workspace);
Will throw an exception with the appropriate parameter name if any of the listed arguments is null. It also has a really neat policy based rules implementation.
e.g.
Enforce.Argument(() => username, StringIs.Limited(3, 64), StringIs.ValidEmail);
My preference would be to just evaluate the condition and pass the result rather than passing an expression to be evaluated and the parameter on which to evaluate it. Also, I prefer to have the ability to customize the entire message. Note that these are simply preferences -- I'm not saying that your sample is wrong -- but there are some cases where this is very useful.
Helper.Validate( firstName != null || !string.IsNullOrEmpty(directoryID),
"The value for firstName cannot be null if a directory ID is not supplied." );
Don't know if this technique transfers from C/C++ to C#, but I've done this with macros:
#define CHECK_NULL(x) { (x) != NULL || \
fprintf(stderr, "The value of %s in %s, line %d is null.\n", \
#x, __FILENAME__, __LINE__); }
In this case, rather than use your own exception type, or really general types like ApplicationException.. I think it is best to use the built in exception types that are specifically intended for this use:
Among those.. System.ArgumentException, System.ArgumentNullException...
Postsharp or some other AOP framework.
It does not apply everywhere, but it might help in many cases:
I suppose that "SomeMethod" is carrying out some behavioral operation on the data "last name", "first name" and "age". Evaluate your current code design. If the three pieces of data are crying for a class, put them into a class. In that class you can also put your checks. This would free "SomeMethod" from input checking.
The end result would be something like this:
public void SomeMethod(Person person)
{
person.CheckInvariants();
// code here ...
}
The call would be something like this (if you use .NET 3.5):
SomeMethod(new Person { FirstName = "Joe", LastName = "White", Age = 12 });
under the assumption that the class would look like this:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public void CheckInvariants()
{
assertNotNull(FirstName, "first name");
assertNotNull(LastName, "last name");
}
// here are your checks ...
private void assertNotNull(string input, string hint)
{
if (input == null)
{
string message = string.Format("The given {0} is null.", hint);
throw new ApplicationException(message);
}
}
Instead of the syntactic sugar of .NET 3.5 you can also use constructor arguments to create a Person object.
Just as a contrast, this post by Miško Hevery on the Google Testing Blog argues that this kind of parameter checking might not always be a good thing. The resulting debate in the comments also raises some interesting points.

Categories