CA1026 (all parameters should have default values) and extension methods - c#

Premise
When using code analysis (or fxCop) with C# optional parameters you can get a warning of CA1026. The short reason1 for this is not suppling all parameters with a default value.
The declaration below rightly generates this warning
public Color GetColor(bool red, bool blue = true, bool green = true)
However there is a situation where you could not supply all parameters with a default, and that is extension methods. So the declaration below generates the warning because of the first parameter:
public static bool ValidateRules(this string s, Rules rules = Rules.Default)
The compiler will not let you specify a default value on the this parameter so the only two solutions is to:
Ignore the warning, which I do not like doing because it leads to bad practices.
Not use extension methods, which I do not like doing because I find extension methods make the code more readible.
Questions
Are the above two options the only
way to solve this?
Is fxCop/Code
Analysis incorrect in it's check?
The long reason

It's not warning you for not having defaults for all parameters - it's warning you for using optional parameters at all.
Personally I would disable this particular warning. When used with care, I think optional parameters are fine. You should think carefully about them particularly in terms of versioning of the default parameter value and in terms of languages which don't support them (including C# before v4) but in many environments the downsides really aren't an issue - and you can end up with much simpler code than by specifying overloads all over the place.

An argument that I am missing in Jon Skeet's answer is also about maintainability: Default values are always filled in with it's value in the IL (intermediate language). This is an issue if you're using external libraries.
Here are steps to reproduce a simple example:
Create a console app
Add a ClassLibrary project to it
Add the following code:
Program.cs
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var obj = new Class1();
Console.WriteLine(obj.Foo());
Console.ReadKey();
}
}
}
and in your Class1.cs
namespace ClassLibrary1
{
public class Class1
{
public string Foo(string str = "http")
{
return str;
}
}
}
If you run it you will see 'http', as expected.
Now change "http" to "https"
Compile only the library (maybe even unload the console project)
Copy the dll from the library's bin folder to the console app's bin folder by hand
Run the console app from the command line, not from within VS!
You will still see http! With ILSpy you can see that http is hardcoded in the console app.
In this case this could lead to a security issue if the developer thinks he is safe by replacing the "http" to "https" in the default value.
So if external libraries are updated always compile your code again. Or just don't use default values.
Just create a separate method:
public string Foo()
{
return Foo("https");
}
public string Foo(string str)
{
return str;
}

You can suppress the warning on a case-by-case basis.

Related

Trying to understand method signature changes spanning assemblies

We ran into a strange problem with a promotion and I'm hoping I'll be able to explain it with code. I want to understand why it behaves in the manner it is.
Assembly 1
public static class Foo
{
public static string DoStuff()
{
// Do something
return "some string";
}
}
Assembly 2:
public class Bar
{
public void SomeMethod()
{
// I realize the below is not what should be done for catching exceptions, as the exception is never thrown due to the return, and seems unnecessary either way...
// this is inherited code and has not been modified to correct.
try
{
var someValue = Foo.DoStuff();
}
catch (Exception)
{
return;
throw;
}
}
}
Requirements changed so that DoStuff would need to take in a parameter, the value of which would change the behavior slightly. Note that only assembly 1.Foo is changing.
New Foo
public static class Foo
{
public static string DoStuff(bool someBool = false)
{
// Do something
return "some string";
}
}
This recompiled fine, and Assembly 2 was able to successfully utilize the changed method signature without complaint. My check in was performed, and project dlls that had changes were promoted (note this was only Assembly 1 dll).
After promotion we found out that Assembly 2 was failing on the Foo.DoStuff() call - unfortunately I cannot provide an exception as the above code was swallowing it.
Even though no actual code changed in Assembly 2, it did seem to have an impact on the dll on recompile, even though the method signature - at least in my mind - is the same due to providing a default value for the new parameter.
I used dotnet peek in order to peek at the "old dll" and the "new dlls" (even though no code change to that assembly and did note a difference in the two DLLs in that in the old DLL, the default value parameter was not supplied, but in the recompiled, the default value parameter was supplied.
I guess my question(s) boil down to this:
Why does the compiled code on assembly 2 change based on a method signature that (at least I think) should be transparent?
Would the method of avoiding a situation like this just be "deploy everything"? Rather than trying to deploy based on code changes? Note that DLLs are not checked in, so there was no actual "code change" to assembly 2, though the DLL was different based on the changes to Assembly 1.
Why does the compiled code on assembly 2 change based on a method signature that (at least I think) should be transparent?
No, it shouldn't. When you don't specify an argument to correspond with an optional parameter, the default value is provided at the call site - i.e. assembly 2 in your case. That's how optional parameters work in C#. It's a pain, but that's life.
Would the method of avoiding a situation like this just be "deploy everything"?
Yes. Basically, the meaning of the source in assembly 2 has changed even though the code itself hasn't - so the compiled code has changed, and it needs to be redeployed.
You can see subtle breaking changes like this in other cases, too - where the same old "client" code compiles against new "receiving" code, but has a different meaning. Two examples:
Suppose you change a method signature from Foo(long x) to Foo(int x) but at the call site, you only call it as Foo(5)... that compiles in both cases, but to different code.
Suppose you have change a method signature from Foo(int x, int y) to Foo(int y, int x) and you have a call of Foo(x: 5, y: 2)... again, the meaning of the code changes from Foo(5, 2) to Foo(2, 5), if you see what I mean. Recompiling the unchanged source code against the new "receiving" code will change the behaviour.
Suppose you have a constant in assembly 1, e.g. public const string Foo = "Foo";. If you change that to public const string Foo = "Bar", but don't recompile assemblies using the constant, those assemblies will still use a value of "Foo". (This goes for default values of optional parameters, too.)

Resharper warns about a null string (System.NullReferenceException)

Just want to be sure that I haven't been coding for too long ... But, this seems highly unlikely :
http://i.imgur.com/TBjpNTX.png
I create the var, check for null, return if it is, so there's no way I can see it as being null at that point :)
Resharper bug ?
Edit:
As per Igal Tabachnik answer, he's right, I'm using the following method extension:
public static bool IsNullOrEmpty(this string target)
{
return String.IsNullOrEmpty(target);
}
I find it much easier to read
if (some_string.IsNullOrEmpty())
// do something here
rather than the:
if (string.IsNullOrEmpty(some_string))
// do something here
Solution:
Igal Tabachnik was right. The only 2 missing pieces were:
Resharper -> Options -> Code Annotations (under Code inspection group) -> turn on for solution.
Give VS a couple of minutes to get refresh everything.
Your code suggests that IsNullOrEmpty() method you're using is your own custom Extension method. The "real" IsNullOrEmpty is a static method of string.
Short answer: if you change it to
if (string.IsNullOrEmpty(input_string))
return "...";
ReSharper will stop complaining.
Long answer: Since this is your own extension method, ReSharper has no way of knowing how the result of this method applies to your code. For this, ReSharper uses code annotations to figure out additional information about the code. One such annotation is called a Contract Annotation, and it is what ReSharper uses to figure out the result of the original string.IsNullOrEmpty() method. You can read more about it in the blog post.
Bottom line, if you want to use your own extension method, but have ReSharper understand it correctly, you have to apply the following Contract Annotation on it:
[ContractAnnotation("null=>true")]
public static bool IsNullOrEmpty(this string input)
{
...
}
Your IsNullOrEmpty()-method seems to be an own invention since the original one is a static method of System.String and not an extension method. ReSharper isn't able to figure that one out though if you use the original one it will see that no null-values can make it past.
var str = value as string;
if (string.IsNullOrEmpty(str))
return;
var unicorn = str.Contains("unicorn");

Is this the right way to overload a method?

I am working on my own command line arguments parser and after reading dozens of articles regarding method overloading I am still not certain if I am doing it right.
Am I getting any benefit from overloading methods this way? I know I could just write the entire thing in a single method (with default value parameters) by branching, but I'm experimenting overloads at the moment and I would like to know whether to continue on this path or not.
public static class MyParsers
{
private static List<string> args;
static MyParsers()
{
args = Environment.GetCommandLineArgs().ToList();
}
public static List<string> ParseOptions()
{
return ParseOptions(false);
}
public static List<string> ParseOptions(bool toLowercase)
{
// DEBUG: Change command line arguments here.
var arguments = args;
return !toLowercase
? arguments
: arguments.MyExtToLower();
}
public static bool OptionExists(string option)
{
return OptionExists(option, false);
}
public static bool OptionExists(string option, bool toLowercase)
{
var list = ParseOptions(toLowercase);
for (var i = 1; i < list.Count; i++)
{
if (list[i].StartsWith(option)) return true;
}
return false;
}
}
Yes that is the correct way to use overloads.
One thing to note about default parameters.
If you have two assemblies, A and B, A calls the function in B.
If you change the default in B:
using default values for parameters you need to recompile both assembly A and B for this change to take effect
using overloads you only need to recompile B.
This is because for default parameters, at compile time the compiler inserts the default values.
Yes, that's fine.
As you already know, you could also use optional parameters with default values, if your overloads only call another method with a default value (this would reduce the number of line of code).
Yep, this is how overloads work.
But a side-node:
Do you need your code to be used from languages which don't support
optional parameters? If so, consider including the overloads.
Do you have any members on your team who violently oppose optional parameters? (Sometimes it's easier to live with a decision
you don't like than to argue the case.)
Are you confident that your defaults won't change between builds of your code, or if they might, will your callers be okay with that?
Source: Should you declare methods using overloads or optional parameters in C# 4.0?
The "problem" with optional parameters is that if the default value is changed in some future version X of your assembly A then any client assemblies C that reference A will need to be recompiled in order to "see" the change -- loading an updated version of A will cause them to call the new methods with the old default values.
If this is not a potential problem then using optional parameters is more convenient. The equivalent version that emulates optional parameters using multiple overloads does not have this issue because in that case the default value is baked into assembly A instead of into C.
I think your style of overloading is fine.
If you thought you might have loads of different parsing arguments (ToUpperCase etc)
rather than have one class with lots of overloaded methods you might consider using object inheritance and have a LowerCaseParser, CamelCaseParser etc...

C# default parameters workaround

Is there a workaround for default parameters? In C++ I would use
int foo(int k, bool check = false)
A tedious workaround would be to overload a function. An easier one? (There is no way just adding the variable and checking the calls of the function!!)
Thanks,
Sun
The C# (before 4.0) didn't support the default parameters. Even in c# 4.0 the default parameters are a bit different than in C++ - they're stored in metadata and, when you reference the assembly with default parameters, they're compiled into your code. So, if the default value was changed in the future, your code will still pass the OLD default value, which may cause the bad effect. So, use the overloaded functions with a single parameter and double parameters and call the one with more parameters passing the default value. Such approach will have a least side effect.
In C#4, you can do the same. So this is allowed:
int foo(int k, bool check = false){
...
}
There are also possible to use named arguments in C#4 so you can call this method in many different ways:
foo(10, true);
foo(10);
foo(k: 10, check: true);
foo(check: true, k: 10);
named arguments are useful if you have several optional parameters, and only want to specify one of them that is not the first optional one, or to improve readability on the calling side.
In C# 4.0 default and named parameters is supported now.
http://msdn.microsoft.com/en-us/library/dd264739.aspx
You can use optional parameters in your assemblies if they are build with MSBuild 4.0 (VS2010) even if you are targeting the .Net 2.0 framework.
The syntax is just like you said:
int foo(int k, bool check = false)
{
}
To elaborate on Denis Mazourick's answer about using default optional parameters in C# 4.0 and how the default values get compiled into the consuming class, try this.
Create a class library with the following code and build it:
public class ClassWithDefaultParameters {
public string Msg { get; set; }
public ClassWithDefaultParameters(string msg = "Hello World") {
Msg = msg;
}
}
public class ClassWithConstructorOverloads {
public string Msg { get; set; }
public ClassWithConstructorOverloads(string msg) {
Msg = msg;
}
public ClassWithConstructorOverloads() : this("Hello World") {}
}
Now create a console application and reference the dll you just built (not the project, but the actual dll). Place this in your code and build the console application.
static void Main() {
var cwdp = new ClassWithDefaultParameters();
var cwco = new ClassWithConstructorOverloads();
Console.WriteLine(cwdp.Msg);
Console.WriteLine(cwco.Msg);
}
When you run the application, the output will be as you expected:
Hello World
Hello World
Now open up the class library, and change both "Hello World" in "Hello Europe". Recompile the library and copy the dll to the output folder of the console application. Do not rebuild the console application.
When you run the console application again, the output will be:
Hello World
Hello Europe
Probably not what you expected! It's not until you rebuild the console application that both lines will print Hello Europe.
I didn't know this and I think I won't use the default parameters because of this. What's worse is that Microsoft doesn't mention this on the MSDN page.
well, there is no easier way, you could use param feature, but it is risky as well.
have a look at example for string.Format() where you can use it like:
stringA.Format("{0} is {1}", str1, str2)
that way you can pass any number of params, but it is quite tricky how u consume it and could be quite error prone

Ignore ObsoleteAttribute Compiler Error

I have an enumeration value marked with the following attribute. The second parameter instructs the compiler to error whenever the value is used. I want this behavior for anyone that implements my library, but I need to use this enumeration value within my library. How do I tell the compiler to ignore the Obsolete error for the couple of uses in my library.
public enum Choices
{
One,
Two,
[ObsoleteAttribute("don't use me", true)]
Three,
Four
}
Solution (Thanks everyone)
public class EnumHack
{
static EnumHack()
{
// Safety check
if (Choices!= (Choices)Enum.Parse(typeof(Choices), "Three"))
throw new Exception("Choices.Three != 3; Who changed my Enum!");
}
[Obsolete("Backwards compatible Choices.Three", false)]
public const Choices ChoicesThree = (Choices)3;
}
Private a separate constant somewhere like this:
private const Choices BackwardsCompatibleThree = (Choices) 3;
Note that anyone else will be able to do the same thing.
What about using #pragma to disable the warning around the specfic code?
#pragma warning disable 0612
// Call obsolete type/enum member here
#pragma warning restore 0612
A note to visitors, this only works with types and enum members. As far as I am aware, this will not work with other type members (e.g. methods, properties, etc).
What I see is that you're using this public enum for private logic, and after obsoleting it, you still need that logic internally.
I see 2 options:
Map it to a private Enum when you use it for your branching logic. You should be able to straight cast from one to the other.
Cast it from an int, thus never using the actual Enum value in your code.
As Jon points out above, anyone using your library can, and WILL (I know where you work), just hack through it anyhow.
It may not be the prettiest solution in the world, but you can try to trick the compiler by assigning values to the enum and then casting on your internal calls. For example this app runs:
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
TestMethod((Choices)3);
}
private static int TestMethod(Choices choice) {
return 1;
}
}
public enum Choices
{
One = 1,
Two = 2,
[ObsoleteAttribute("don't use me", true)]
Three = 3,
Four = 4
}
}
I thought that Enum.Parse would work but it gets a run-time error, so don't do this:
(Choices)Enum.Parse(typeof(Choices), "Choices.Three")
I don't have experience with obsolete enums so I would recommend some pretty good testing around this.
TheSoftwareJedi correctly notes that this won't work with obsolete attribute set to be an error. The following "answer" only works when the obsolete notification is raised as a warning.
From Visual Studio you can do this on a per-project basis:
Go to the Project Properties page for the project you want to be able to suppress the obsolete warning on.
Go to the Build tab: Errors and Warnings : Suppress Warnings
Enter the warning number, 0612 in this case.
Other projects will continue to get the obsolete warning but this project will not. Note that this will disable ALL obsolete warnings.

Categories