Optional parameter & params enum in method signature in C# - c#

My current issue is that I cannot use the method I wrote in the way I expected I could.
This is my signature :
IList<FileInfo> GetFiles(string name = "*", params SignalState[] states);
If relevant, please note that SignalState is an enum.
I would expect to be able to call this method with either one argument (string OR SignalState), none, or both of them. It works if I use both arguments, if I use none of them, or if I use only the name.
Right now I would expect that only providing a SignalState would work, because the compiler would assume the first parameter is optional and my SignalState fits the second parameter of the signature.
It does not work, and the error is quite clear, its expecting a name.
So I have tried changing the signature to this :
IList<FileInfo> GetFiles([Optional] string name, params SignalState[] states);
and obviously add a bit of logic to handle my wildcard default value for the string, but I still face the exact same problem.
I have also tried switching the two paramters, params SignalState first and the string last, but the compiler specifically says params should be last and shows an error in the signature.
Could someone explain to me why it is behaving the way it is ? I would expect to be able to jump to the second param since the first one is optional.
Is my only solution to implement as many overloads as necessary to be able to call my method with only a SignalState ? Or am I missing something in how optional parameters work ?

Related

Why would one want to use string.Format with only one parameter?

I encountered the following code string in a project's code:
var result = string.Format(source);
with variable 'source' being a string
I can't understand what useful this line is doing. As I always thought, we need at least two parameters for string.Format method to have some useful output.
ReSharper is not highlighting this as a something redundant so it seems that this line might have some purpose which I can't grasp at the moment. (Or maybe ReSharper just doesn't handle this case specifically)
Why would one want to use string.Format with only one parameter?
Perhaps the overloaded function
public static string Format(string format, params object[] args);
is allowing that code to compile. I cann't imagine that string.Format(source) would be helping in any meaningful way.

Changes on overloaded method behaviour between visual studio 2013 and 2015

Have have big problem with Visual Studio 2013 and 2015. In one class, i have defined this two methods:
public List<T> LoadData<T>(string connectionStringName = "", string optWherePart = "", params object[] parameter)
public List<T> LoadData<T>(string optWherePart, params object[] parameter)
I only want to call the second method like this:
....LoadData<Config_Info>("ConfigName LIKE 'Version' AND UserName LIKE '' AND PlugInName Like ?", parameter: ProductName);
If i go to definition in Visual Studio 2013, i come to the second method declaration, but in Visual Studio 2015, i come to the first. Both solutions are
absolutly identically.
Even the compiled result is different, so if i compile the same solution with VS 2015, the program stops working.
This is a very strange behaviour.
Does any one has an idea, what the differences are?
This is based on the C# 5 specification, but since the C# 6 specification doesn't appear to have been published yet that's the best I can do. It's also an attempt to invoke Cunningham's Law.
As a preliminary, in the language of s7.5.3.1 ("Applicable Function Member") of the spec, both function members are applicable (either of them could be called if the other didn't exist) in their expanded form (the params object[] can't be fulfilled by the string ProductName so is converted to an object argument).
Thus we move on to s7.5.3.2 ("Better Function Member") in order to decide which of the two is the better function to call.
Firstly, a stripped-down argument list A is constructed containing just the argument expressions themselves in the order they appear in the original argument list:
{ string "ConfigName [...]", string ProductName }
Next, [p]arameter lists for each of the candidate function members are constructed in the following way:
The expanded form is used if the function member was applicable only in the expanded form.
Optional parameters with no corresponding arguments are removed from the parameter list
The parameters are reordered so that they occur at the same position as the corresponding argument in the argument list.
This gives us the following:
{ string connectionStringName, object parameter } (optWherePart removed, params expanded)
{ string optWherePart, object parameter } (params expanded)
We then have a sequence of comparisons to make to decide which of these is the better function member. Calling one Mp and one Mq, these go as follows:
If Mp is a non-generic method and Mq is a generic method, then Mp is better than Mq.
No difference here
Otherwise, if Mp is applicable in its normal form and Mq has a params array and is applicable only in its expanded form, then Mp is better than Mq.
No difference here; both are in their expanded form
Otherwise, if Mp has more declared parameters than Mq, then Mp is better than Mq. This can occur if both methods have params arrays and are applicable only in their expanded forms.
Not 100% on this one. Both our argument lists use 2 of the parameters from the original function definitions. I think this is solely meant to distinguish between one case where both arguments went into the same params array and one where one went into the array and one went into a normal argument.
Otherwise if all parameters of Mp have a corresponding argument whereas default arguments need to be substituted for at least one optional parameter in Mq then Mp is better than Mq.
Aha! Our first argument list is missing optWherePart, which needed a default argument, so the second argument list is better! So VS2015 is wrong!
... but wait. What does this last bullet even mean? Mp and Mq are specifically parameter lists where [o]ptional parameters with no corresponding arguments are removed. There is no way either of them could not have a corresponding argument because if they didn't, they'd have been removed.
In conclusion, I can't tell whether this is a bug in the old compiler, the new compiler... or the C# specification.
I've found a blog post by SLaks that also seems to think the old behaviour was a bug. The blog states that Roslyn had fixed this by making the compiler fail, and that's not what I see any more. Maybe they changed their minds?
Edit: Update! My Roslyn bug report resulted in a change to the compiler to ensure that, in this case, the second overload is picked. This appears to be because of the default arguments need to be substituted wording above. I still think the spec is ambiguous, so I'm disappointed that only a code change was made (and not a spec change, or even a discussion about why the second overload is the better one), but at least the VS2015 runtime beaviour is now the same as it was in VS2013.

How to read MSDN API correctly. (eg. IDbSetExtensions.AddOrUpdate)

For example, if you go to IDbSetExtensions.AddOrUpdate Method (IDbSet, Expression>, TEntity[]) page on MSDN -- http://msdn.microsoft.com/en-us/library/hh846514(v=vs.103).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1 --, you see that it takes three params. IDbSet, Expression and TEntity.
But what people usually write is like the below.
AddOrUpdate(item => new{item.Text}, itemArray); -- for the "seed method" within migrations.
My questions are:
How come there's only 2 params provided, not 3 and it's still ok?
What will be the difference between "AddOrUpdate(item => item.Text, itemArray)" and "AddOrUpdate(item => new{item.Text}, itemArray)" where first one there's no new operator.
When you're programming, do I need to know what every single line (within a project template) is doing?
I started project using template, so I don't have complete understanding of what it's doing but it sure takes time to dissect the whole template.
Method you're looking at is an extension method. That's why you can call it with the first parameter missing.
If you look closely, you can see that the method is static, which means it should be called using the class name, which is IDbSetExtensions.AddOrUpdate. However, because it is an extension method (this modifier in front of the first method argument makes that happen), you can call it as if it was an instance method of the type of the first method argument, in this case IDbSet<TEntity>.
Read more about extension methods on MSDN: Extension Methods (C# Programming Guide)
For AddOrUpdate(item => item.Text, itemArray) generic type TObject will get inferred to be whatever the type of item.Text is (probably a string). For AddOrUpdate(item => new{item.Text}, itemArray) is will be inferred as anonymous type, with one property.
You definitely should.
How come there's only 2 params provided, not 3 and it's still ok?
The method call AddOrUpdate(item => new{item.Text}, itemArray) only passes two parameters, which is the same number of parameters as AddOrUpdate(item => item.Text, itemArray).
What's the difference
The method call with new {item.Text} returns an anonymous type with (assuming item.Text is a string) a string property called Text that has the value of item.Text. The other method returns a string.
Do you need to know what every single line is doing?
No. Only, the compiler needs to know. But it will help you write software if you know what the code does.

Force my code to use my extension method

I'm using BitFactory logging, which exposes a bunch of methods like this:
public void LogWarning(object aCategory, object anObject)
I've got an extension method that makes this a bit nicer for our logging needs:
public static void LogWarning(this CompositeLogger logger,
string message = "", params object[] parameters)
Which just wraps up some common logging operations, and means I can log like:
Logging.LogWarning("Something bad happened to the {0}. Id was {1}",foo,bar);
But when I only have one string in my params object[], then my extension method won't be called, instead the original method will be chosen.
Apart from naming my method something else, is there a way I can stop this from happening?
The rules about how overloaded methods are resolved to one (or an error) are complex (the C# specification is included with Visual Studio for all the gory details).
But there is one simple rule: extension methods are only considered if there is no possible member that can be called.
Because the signature of two objects will accept any two parameters, any call with two parameters will match that member. Thus no extension methods will considered as possibilities.
You could pass a third parameter (eg. String.Empty) and not use it in the format.
Or, and I suspect this is better, to avoid possible interactions with additions to the library (variable length argument list methods are prone to this) rename to LogWarningFormat (akin to the naming of StringBuffer.AppendFormat).
PS. there is no point having a default for the message parameter: it will never used unless you pass no arguments: but that would log nothing.
Declared methods are always preceding extension methods.
If you want to call the extension regardless of the declared method, you have to call it as a regular static method, of the class that declared it.
eg:
LoggerExtensions.LogWarning(Logging, "Something bad happened to the {0}. Id was {1}",foo,bar);
I assume that the extension is declared in a class named LoggerExtensions
Provided that I think a method with a different name is the way to go (easier to read and maintain), as a workaround you could specify parameters as a named parameter:
logger.LogWarning("Something bad happened to the {0}.", parameters: "foo");

Mixing Out and Named Parameters in C#: Why Does Out Parameter Need to be Named As Well?

Short Version: A named argument following an out argument gives a compiler error, but I cannot find any support for this behaviour in the language specification.
Long Version:
I'm using the Enum.TryParse<TEnum> three parameter overload, but I would prefer to name the ignoreCase parameter to make my code clearer, a call like:
MyEnum res;
b = Enum.TryParse<MyEnum>(inputString, true, out res);
leaves the meaning of the boolean unclear (unless this method is known1). Hence I would like to use:
b = Enum.TryParse<MyEnum>(inputString, out res, ignoreCase: true);
However the compiler reports this as an error:
Named argument 'ignoreCase' specifies a parameter for which a positional argument has already been given
and the IDE highlights the ignoreCase parameter. VS2010 targeting .NET 4, and VS11 Beta targeting either 4 or 4.5 all give the same result. In all cases naming the out parameter removes the error.
b = Enum.TryParse<MyEnum>(inputString, result: out res, ignoreCase: true);
I've tried this across a number of different methods (including avoiding generics)2, both from the framework and in my assembly: always the same result: an out parameter followed by a named parameter gives an error.
I can see no reason for this error, and §7.5.1 Argument Lists of the C# Language Specification: Version 4.0 does not seem to provide any reason why an out followed by a named parameter should give an error. The text of the error seems to support an interpretation as a bug: there is no positional argument which could be a valid match for ignoreCase.
Is my reading of the specification wrong? Or is this a compiler bug?
C# 7.2 Update
This restriction on all named arguments have to follow positional arguments when calling was lifted with C# 7.2.
See https://learn.microsoft.com/en-gb/dotnet/csharp/whats-new/csharp-7-2#non-trailing-named-arguments.
1 Hence the advice in the Framework Design Guidelines to prefer enum parameters.
2 Eg: given:
private static void TestMethod(int one, float two, out string three) {
three = "3333";
}
this this call also gives the same error on the named parameter unless the out parameter is also named:
TestMethod(1, out aString, two: 1.0f);
Named parameters do not allow you to "skip" positional arguments.
Your code is parsed as passing the first two arguments—value and ignoreCase, then passing ignoreCase again.
It has nothing to do with the outness of the parameter.
You can fix it by passing the last parameter as named too.
Every positional argument needs to match, if you start rearranging the order by naming the arguments, you must rearrange all the arguments following the one you named.
So this line of code:
b = Enum.TryParse<MyEnum>(inputString, out res, ignoreCase: true);
Tries to match out res with ignoreCase, and then you come along naming that parameter again, which trips the compiler up. Likely there is another error just lurking behind the first one, that out res is not a match for ignoreCase.
So if you want to "skip" ignoreCase when dealing with positional arguments, you must name the out res argument as well.

Categories