Why is params 'less performant' than a regular array? - c#

If you go right now and type string.Format into your IDE, you'll see that there are 4 different overloads: one taking a string and object, another taking a string and two objects, then one taking three objects, and finally one that uses params. According to this answer, this is because params generates 'overhead', and some other languages may not support it.
My question is, why can't a method call like this:
void Foo()
{
Bar(1, 2, 3);
}
void Bar(params int[] args)
{
// use args...
}
Be essentially transformed at compile time to
void Foo()
{
Bar(new[] { 1, 2, 3 });
}
void Bar(int[] args)
{
// use args...
}
? Then it wouldn't create any overhead except for the array creation (which was necessary anyway), and would be fully compatible with other languages.
The number of arguments is already known at compile-time, so what's preventing the C# compiler from doing some kind of string substitution and making the first scenario essentially syntactic sugar for the second? Why did we have to implement hidden language features specifically to support variadic arguments?

The title makes an incorrect assumption.
Both a params and a non-params methods take an array; the difference is the compiler will emit the IL to create an array implicitly when making a params method call. An array is passed to both methods, as a single argument.
This can be seen in this .NET Fiddle (view "Tidy Up -> View IL").
using System;
public class Program
{
public static void Main()
{
var a1 = 1;
var a2 = 2;
var a3 = 3;
with_params(a1,a2,a3);
no_params(new [] {a1,a2,a3});
}
public static void with_params(params int[] x) {}
public static void no_params(int[] x) {}
}
In both cases the IL is identical; a new array is created, it is populated, and the array is supplied to the invoked method.
There is an "exception" to this identical IL generation in that the compiler can move out constant-valued arrays when used in the non-parameter form and use 'dup' initialization, as seen here. However, a new array is supplied as the argument in both cases.

Related

Why do I have to type-cast a strongly typed function using a dynamic input-parameter?

I have a converter method:
MyPoco Convert(dynamic p) => new MyPoco { A = p.X, B = p.Y };
void Test()
{
dynamic item = new { X = 1, Y = 2 };
var poco = (MyPoco)Convert(item);
}
I have to explictly cast the result to MyPoco, otherwise poco will become a dynamic variable, too.
But, if I inline the Convert method;
void Test()
{
MyPoco Convert(dynamic p) => new MyPoco { A = p.X, B = p.Y };
dynamic item = new { X = 1, Y = 2 };
var poco = Convert(item);
}
I don't need to cast ConvertItem to MyPoco. Is there a reason for this behavior? It should be easy for the compiler to know that Convert return-type is MyPoco, right?
There is a difference between them which could be the reason - local functions don't support overloading. I think it is important - imagine we have two methods with the same name and with different input and output types
static void Main(string[] args)
{
dynamic d = null;
var result = Hello(d);
Console.WriteLine("Hello World!");
}
static string Hello(string s)
{
return s;
}
static int Hello(int i)
{
return i;
}
It means the result may be string or int - we don't know it in compile time.
While for the following code we get the error that local variable or function already declared
static void Main(string[] args)
{
string Hello(string s)
{
return s;
}
int Hello(int s) // error - local variable or function with the same name already declared
{
return s;
}
dynamic d = null;
var result = Hello(d);
Console.WriteLine("Hello World!");
}
We can only write something like this
static void Main(string[] args)
{
string Hello(string s)
{
return s;
}
dynamic d = null;
var result = Hello(d);
Console.WriteLine("Hello World!");
}
So when compiler sees the local Hello(...) call it knows that return type is string.
Upd:
Regarding the compiler ability to infer correct type in case of dynamic.
I think yes, it is possible for compiler to catch such cases - if we know in compile time that there is the only one method,
there is no chance that in runtime another one will appear.
I could imagine e.g. the method we call is in another assembly, and in runtime we loaded newer version which has different signature - with dynamic it will work, but for the, say, private static method with no overloads I think we could infer non-dynamic type.
But I think, it is was decided to implement it that way for the sake of simplicity - it is easier to keep in mind simple rule - everything that touches dynamic - dynamic.
For local functions for simplicity I think it would be easier to also have them dynamic. I think it is just decision made by different people implementing that.
I checked the roslyn source code trying to find information about that.
The place where it is defined is Binder_Invocation.cs, BindMethodGroupInvocation method.
For local function the following method is called
private BoundExpression BindLocalFunctionInvocationWithDynamicArgument(
SyntaxNode syntax,
SyntaxNode expression,
string methodName,
BoundMethodGroup boundMethodGroup,
DiagnosticBag diagnostics,
CSharpSyntaxNode queryClause,
MethodGroupResolution resolution)
{
// Invocations of local functions with dynamic arguments don't need
// to be dispatched as dynamic invocations since they cannot be
// overloaded. Instead, we'll just emit a standard call with
// dynamic implicit conversions for any dynamic arguments. There
// are two exceptions: "params", and unconstructed generics. While
// implementing those cases with dynamic invocations is possible,
// we have decided the implementation complexity is not worth it.
// Refer to the comments below for the exact semantics.
As you can see the also say about overloading, but for the normal method call no any information about the reason
else
{
if (HasApplicableConditionalMethod(resolution.OverloadResolutionResult))
{
// warning CS1974: The dynamically dispatched call to method 'Goo' may fail at runtime
// because one or more applicable overloads are conditional methods
Error(diagnostics, ErrorCode.WRN_DynamicDispatchToConditionalMethod, syntax, methodGroup.Name);
}
// Note that the runtime binder may consider candidates that haven't passed compile-time final validation
// and an ambiguity error may be reported. Also additional checks are performed in runtime final validation
// that are not performed at compile-time.
// Only if the set of final applicable candidates is empty we know for sure the call will fail at runtime.
var finalApplicableCandidates = GetCandidatesPassingFinalValidation(syntax, resolution.OverloadResolutionResult,
methodGroup.ReceiverOpt,
methodGroup.TypeArgumentsOpt,
diagnostics);
if (finalApplicableCandidates.Length > 0)
{
result = BindDynamicInvocation(syntax, methodGroup, resolution.AnalyzedArguments, finalApplicableCandidates, diagnostics, queryClause);
}
else
{
result = CreateBadCall(syntax, methodGroup, methodGroup.ResultKind, analyzedArguments);
}
They create dynamic if there is at least one canditate.
So, as I said, I think it could be done non-dynamic, but the people who implemented it originally kept it dynamic, probably for simplicity.
What you could do in order to find more details is to try to implement the case when no overloading methods, changing the code
if (finalApplicableCandidates.Length > 0)
{
result = BindDynamicInvocation(syntax, methodGroup, resolution.AnalyzedArguments, finalApplicableCandidates, diagnostics, queryClause);
}
by adding the check if Length == 1 then call BindInvocationExpressionContinued instead of BindDynamicInvocation
and run the tests and check if something fails, maybe it helps.(I didn't even manage to get the roslyn project built, dotnet core is a bit weird)
P.S.
According to this
if (boundMethodGroup.TypeArgumentsOpt.IsDefaultOrEmpty && localFunction.IsGenericMethod)
{
Error(diagnostics, ErrorCode.ERR_DynamicLocalFunctionTypeParameter, syntax, localFunction.Name);
return BindDynamicInvocation(
For local function we could get dynamic instead of concrete type.
If you type something like this
static void Main(string[] args)
{
int TestFunc<T>(T data)
{
return 1;
}
dynamic d = 2;
var r = TestFunc(d);
}
Yes, it will give the error, but if you check the inferred type it of r will show dynamic))

How to ensure compilation error on signature change where 'params' keyword is used

I have a method like this:
public void Foo(params string[] args) {
bar(args[0]);
bar(args[1]);
}
The new requirements lead to a change like this:
public void Foo(string baz, params string[] args) {
if("do bar".Equals(baz)) {
bar(args[0]);
bar(args[1]);
}
}
The problem is that even though I've changed the method signature, no compilation errors occur, which is correct of course, but I want there to be compilation errors for every call to Foo method where the argument baz has not been specified. That is, if a call to Foo before the change was this one:
Foo(p1,p2); //where p1 and p2 are strings
it now needs to be this one:
Foo(baz,p1,p2);
If it wouldn't be changed in this way, p1 would be assigned to baz, and the params array args would be of length 1 and an OutOfBounds exception would be thrown.
What's the best way to change the signature and ensure that all the calling code is updated accordingly? (The real scenario is where Foo lives in an assembly shared by many projects automatically built on a build server. A compilation error would thus be an easy way to detect all the code that needs to be touched to accomodate the change.)
Edit:
As Daniel Mann and others pointed out, the example above suggests that I should not use params at all. So I should explain that in my real world example it's not always the case that args needs to have two elements, as far as the logic in Foo is concerned args can contain any number of elements. So let's say this is Foo:
public void Foo(string baz, params string[] args) {
if("do bar".Equals(baz)) {
int x = GetANumberDynamically();
for(int i = 0; i<x; i++)
bar(args[i]);
}
}
Here's the solution. Do not change the former method signature, just add the Obsolete attribute with both arguments specified.
[Obsolete("Use Foo(string, params string[]) version instead of this", true)]
public void Foo(params string[] args) {
bar(args[0]);
bar(args[1]);
}
Then create a new method with a new signature.
public void Foo(string baz, params string[] args) {
if("do bar".Equals(baz)) {
bar(args[0]);
bar(args[1]);
}
}
The second argument in the Obsolete attribute ensures a compilation error. Without it it just causes a compilation warning. More info about the attribute is available on MSDN.
EDIT:
Based on discussion in comments below, Daniel Mann came up with an interesting problem.
That wouldn't solve the problem. What about if you call Foo("a", "b")? In that case, it will still call the non-obsolete method with only two arguments, and cause the same problem.
I would advise to check if there is more then one argument passed through args before calling bar.
The easiest solution is to not use the params keyword if you have required parameters.
Obviously, you're expecting args to contain at least two parameters. It's safe to say that those are required. Why not have a method signature like this?
public void Foo(string baz, string requiredArgument1, string requiredArgument2, params string[] optionalArguments)
That removes the ambiguity: It will always require at least 3 arguments.
Another option I hadn't even thought of for some reason is to use named parameters. Obviously, all of your code would have to explicitly do so, but you could do this:
Foo(baz: "bar", args: new [] {"a", "b", "c"});

Any tips to make working with Tuples easier in C#?

Often you want to send multiple values but due to low use (i.e. it is only used in one or two places), it's hard to justify creating a new type.
The Tuple<...> and KeyValuePair<,> type are very useful, but there isn't real language support for them.
Well sort of, a nice trick to use for Lists of tuples is to create a type that extends the List and adding a custom add method:
e.g.
public class TupleList<T1,T2> : List<Tuple<T1,T2>>{
public void Add(T1 key, T2 value){
base.Add(Tuple.Create(key, value));
}
}
This means that if I have a method that takes an IEnumerable<Tuple<int,string>>, I can use the following code to quickly build up the list like so::
Foo(new TupleList<int,string>{{1,"one"},{2,"two"},{3,"three"}});
This makes winding values into a tuple list easier as we don't have to constantly keep saying Tuple.Create, and gets us almost to a nice functional languages syntax.
But when working with a tuple it is useful to unwind it out into its different components. This extension method might be useful in this respect::
public static void Unwind<T1,T2>(this Tuple<T1,T2> tuple,out T1 var1,out T2 var2)
{
var1 = tuple.Item1;
var2 = tuple.Item2;
}
But even that's annoying as out parameters are not variant at all. That is if T1 is a string, I can't send in an object variable even though they are assignable, when as I can do the unwinding by hand otherwise. I can't really suggest a reason why you might want this variance, but if its there, I can't see why you would want to lose it.
Anyone have other tips to making working tuples, or tuple like objects easier in C#?
An important potential use for tuples might be generic memoization. Which is very easy in languages like F#, but hard in C#.
I'm currently using Tuples to supply a MethodBase and an array of tokens (constants, objects, or argument tokens), supplied to a dynamicly built object to construct certain member fields.
Since I wanted to make the syntax easier on API consumers, I created Add methods that can take a ConstructorInfo or a MethodInfo and a params array of objects.
Edit:
Eric Lippert as usual has excellent motivation for using Tuples here and he even says what I suspected there really is no support:
What requirement was the tuple designed to solve?
In C# you can alias closed generic types, which Tuple is, this enables you to provide some better insight to what is intended. Doesn't change code much, but if you look at the example below the intent of what GetTemp is returning is better.
Without alias:
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var result = GetTemp(10, 10);
Console.WriteLine("Temp for {0} is {1}", result.Item2, result.Item1);
}
// You give a lat & a long and you get the closest city & temp for it
static Tuple<double, string> GetTemp(double lat, double #long)
{
// just for example
return Tuple.Create(10d, "Mordor");
}
}
}
With alias:
namespace ConsoleApplication1
{
using CityTemp = Tuple<double, string>;
class Program
{
static void Main(string[] args)
{
var result = GetTemp(10, 10);
Console.WriteLine("Temp for {0} is {1}", result.Item2, result.Item1);
}
// You give a lat & a long and you get the closest city & temp for it
static CityTemp GetTemp(double lat, double #long)
{
// just for example
return new CityTemp(10, "Mordor");
}
}
}
Use Mono! They have experimental support for binding variables to tuple members so you could call a method like
Tuple<string, string, string, int, string> ParseUri (string url);
using code like
(user, password, host, port, path) = ParseUri (url);
There will be an awesome tuple feature coming with c#7 / visual studio 15.
basically you can do soething like that
static (int x, int y) DoSomething()
{
return (1, 2);
}
static void Test()
{
var cool = DoSomething();
var value = cool.x;
}
Read according post

How exactly keyword 'params' work?

The following code sample prints:
T
T[]
T[]
While first two lines are as expected, why compiler selected param array for a regular array?
public class A
{
public void Print<T>(T t)
{
Console.WriteLine("T");
}
public void Print<T>(params T[] t)
{
Console.WriteLine("T[]");
}
}
class Program
{
static void Main(string[] args)
{
A a = new A();
a.Print("string");
a.Print("string","string");
a.Print(new string[] {"a","b"});
}
}
Under the hood
a.Print("string","string");
is just syntactic sugar for
a.Print(new string[]{"string","string"});
EDIT: Like I said, the params keyword only automagically creates the array for you, you tell the compiler: either accept an array of T directly or use the X input params to construct that array.
It addition to what others have said, the params keyword also causes a ParamArrayAttribute to the generated for array parameter. So, this...
public void Print<T>(params T[] t) { }
Is generated by the compiler as...
public void Print<T>([ParamArray] T[] t); { }
It is that attribute which indicates to the compiler and the IDE that the method can be called using simpler syntax...
a.Print("string", "string");
rather than...
a.Print(new string[] { "string", "string" });
I think this actually has more to do with type inference than with the params keyword. The inference engine assumes on the third line that the type of T is string[] and therefore passes it to the first method.
try Console.WriteLine(typeof(T)) if you don't believe me
when having the params keyword, the compiler will do a little bit of translation for the formal function declaration, as well as the actual function call.
Formal function declaration:
Under the hood, the IL will be translated to essentially the same as
public void Print<T>(T[] array);
Except, when compiling, the actual function call will be checked for syntax translation. Meaning,
a.Print("string1", "string2");
Becomes the same IL code as
a.Print(new string[]{"string1", "string2"});
That is why line 2 and 3 are the same output, because under the hood, they got translated to the exact same IL.
Question about why line 3 is not print "T" is because, .NET compiler will always try to find the best overloaded match, and so both line 2 and 3 called to the T[] version instead of the plain T.
Exactly as arul said. If you open up the project in reflector, you'll see the following:
a.Print<string>(new string[] { "string", "string" });
Params allows you to pass multiple objects of the same type. it is a shortcut way of passing array

Anything like the c# params in c++?

That is the question.
Background: C# Params
In C#, you can declare the last parameter in a method / function as 'params', which must be a single-dimension array, e.g.:
public void SomeMethod(int fixedParam, params string[] variableParams)
{
if (variableParams != null)
{
foreach(var item in variableParams)
{
Console.WriteLine(item);
}
}
}
This then essentially allows syntactic sugar at the call site to implicitly build an array of zero or more elements:
SomeMethod(1234); // << Zero variableParams
SomeMethod(1234, "Foo", "Bar", "Baz"); // << 3 variableParams
It is however still permissable to bypass the sugar and pass an array explicitly:
SomeMethod(1234, new []{"Foo", "Bar", "Baz"});
Yes. In standard C++, you can use va_arg and the ... syntax. See MSDN for details.
For C++/CLI, There is a shortcut for this.
You do this as:
void TheMethod( String^ firstArgument, ... array<Object^>^ variableArgs );
See this blog post for details.
For unmanaged C++ with the same convenient syntax, no.
But there is support for variable argument lists to functions in C++.
Basically you declare a function with the last parameter being an ellipsis (...), and within the body of the function use the va_start()/va_arg() calls to parse out the supplied parameter list.
This mechanism is not type safe, and the caller could pass anything, so you should clearly document the public interface of the function and what you expect to be passed in.
For managed C++ code, see Reed's comments.
Nowadays, with modern C++, you can use modern type-safe practices for variadic functions.
Use either variadic templates or std::initializer_list if all your arguments have the same type
With variadic template, you use recursion to go through a variadic parameter list. Variadic template example:
template<class T>
void MyFoo(T arg)
{
DoSomething(arg);
}
template<class T, class... R>
void MyFoo(T arg, R... rest)
{
DoSomething(arg);
// If "rest" only has one argument, it will call the above function
// Otherwise, it will call this function again, with the first argument
// from "rest" becoming "arg"
MyFoo(rest...);
}
int main()
{
MyFoo(2, 5.f, 'a');
}
This guarantees that if DoSomething, or any other code you run before the recursive call to MyFoo, has an overload for the type of each argument you pass to the function MyFoo, that exact overload will get called.
With std::initializer_list, you use a simple foreach loop to go through the arguments
template<class T>
void MyFoo(std::initializer_list<T> args)
{
for(auto&& arg : args)
{
DoSomething(arg);
}
}
int main()
{
MyFoo({2, 4, 5, 8, 1, 0}); // All the arguments have to have the same type
}
Yes! C++11 and above allows function templates to take a type-safe variable number of arguments, creating what's known as a "parameter pack". You can unpack that into, e.g., a std::array and get something a lot like you are looking for:
struct S {
template <typename... Args>
void SomeMethod(int someParam, Args&&... args) const {
//^ The "&&" here is a "forwarding reference" because Args is a template.
// Next line isn't required but arguably helps with error messages:
static_assert((std::is_convertible_v<Args, std::string_view> && ...), "All args in the pack must convert to string_view.");
// Convert args... to a std::array<std::string_view, N>:
const auto variableParams = std::array<std::string_view, sizeof...(args)>{std::forward<Args>(args)...};
if (not variableParams.empty()) { //< Not needed because it's a nop to iterate over an empty array:
for (const auto& item : variableParams) {
fmt::print("{}\n", item);
}
}
}
};
int main() {
S s;
s.SomeMethod(42, "foo", "bar", "baz");
}
output:
foo
bar
baz
https://godbolt.org/z/PbPb7qTv9
In C++20, you can use concepts to get a better error message and to be more concise:
void SomeMethod(int someParam,
std::convertible_to<std::string_view> auto&&... args) const {
Happy case: https://godbolt.org/z/aTG74Wx7j
Error case: https://godbolt.org/z/jToxYMThs
There is a named parameters library in boost (if I understood correctly what params in C# are). It allows writing functions like this:
int y = lib::f(_name = "bob", _index = 2);
Can't tell anything about if there is a significant overhead involved.

Categories

Resources