C# Methods with infinite parameters array access - c#

After learning how to Create a method with infinite parameters, I wonder is it legal to store the parameter array into a array. Will it cause any problem, since I don't see many people use this approach.
Code below :
class Foo
{
private String[] Strings;
public Foo(params String[] strings)
{
Strings = strings;
}
...
}

That's fine - it's just an array.
All the compiler does with a parameter array is convert a call like this:
Foo("x", "y");
into:
Foo(new string[] { "x", "y" });
That's really all there is to it. Anything you'd expect to be appropriate with the second call is fine with a parameter array.
Arrays passed into public methods are rarely suitable to store directly due to all arrays being mutable - but that's a matter of how you handle mutable parameter types rather than being specific to parameter arrays.

Related

Multiple strings vs Dictionary of strings as method parameters

I have some methods that take 20 or more strings as parameters. I was wondering what works better: passing 20 string parameters to the method or putting them all in a dictionary and passing it as only parameter.
Multiple strings:
public Boolean isNice(string aux, string aux2, string aux3, string aux4, string aux5, string aux6, string aux7,
string aux8, string aux9, string aux10, string aux11, string aux12, string aux13, string aux14, string aux15, string aux16)
{
string foo1 = aux;
string foo2 = aux2;
// etc
return true;
}
public void yeah()
{
string aux = "whatever";
string aux2 = "whatever2";
// etc
isNice(aux, aux2, ..., ..., ...);
}
Dictionary of strings
public Boolean isNice(Dictionary<string, string> aux)
{
string foo1 = aux["aux1"];
string foo2 = aux["aux2"];
// etc
return true;
}
public void yeah()
{
string aux = "whatever";
string aux2 = "whatever2";
// etc
Dictionary<string, string> auxDict = new Dictionary<string,string>();
auxDict.Add("key1", aux);
auxDict.Add("key2", aux2);
// etc
isNice(auxDict);
}
My question is regarding performance, readability and code simplicity.
Right now I'm using multiple strings: should I use dictionaries instead?
This depends. Are all 20 parameters required for the function to work?
If so, create a data type that can communicate all 20 values and pass in an instance of that data type. You could create helper classes to easily initialize that type of object. You can easily pass in a new instance of that data type, and provide flexible ways to initialize the type:
isNice(new niceParams
{
aux1 = "Foo",
aux2 = "Bar"
// ...
}
);
If not, put the optional parameters at the end of the signature, and give them default values.
public Boolean isNice(string req1, string req2, string optional1 = null)
This way, you have overloads to specify exactly which values you want to provide.
Another benefit of this is you can used named parameters to call the function:
isNice(req1, req2, optional1: "Foo", optional15: "Bar");
With that said, I would not use a dictionary. It forces the caller to understand the signature, and completely breaks any compiler type safely. What if required values aren't provided? What if a key is misspelled? All this checking has to now be done at runtime, causing errors that can only be caught at runtime. To me, it seems to be asking for trouble.
The main difference is that in case when you have 20 string parameters the compiler will ensure that all of them are explicitly set, even if they are set to null. In case of passing a collection the compiler will not be able to detect that somebody has forgotten to set the aux17 parameter: the code that uses a dictionary-based API would continue to compile, so you would be forced to add an extra check at run-time.
If it is OK with your code to not have a compiler check, for example, because all your string values are optional, then a collection-based approach is easier to maintain.
The difference in speed cannot be predicted until you implement the change. A collection-based approach would perform an additional memory allocation, so it would consume more CPU cycles. On the other hand, the difference may be too small to have a real impact on the overall performance of your program.
Note that since your parameters are named uniformly, it appears that they may be placed in a "flat" collection, rather than a dictionary. For example, you could make an API taking a list or an array of strings. In case of an array, you could also make your method take variable number of parameters, so that the callers could use the old syntax to call your method:
public bool isNice(params string[] args)

LINQ statement no longer works after being wrapped into an extension method

I had a need for a method that could take a collection of strings, and replace all occurrences of a specific string with another.
For example, if I have a List<string> that looks like this:
List<string> strings = new List<string> { "a", "b", "delete", "c", "d", "delete" };
and I want to replace "delete" with "", I would use this LINQ statement:
strings = (from s in strings select (s=="delete" ? s=String.Empty : s)).ToList();
and it works great. But then I figured I should make it an extension method, since I'd likely use it again later. In this case, I just want to write the following:
strings.ReplaceStringInListWithAnother( "delete", String.Empty);
While my code compiles, and the LINQ statement works inside of the extension method, when I return the collection reverts back to its original contents:
public static void ReplaceStringInListWithAnother( this List<string> my_list, string to_replace, string replace_with)
{
my_list = (from s in my_list select (s==to_replace ? s=replace_with : s)).ToList();
}
So it would seem that I just modified a copy of the List... but when I looked at the code for Pop, it modifies the collection similarly, yet the changes stick, so my assumption was that my method's parameter declarations are correct.
Can anyone explain what I am doing wrong here?
The LINQ statement you wrote does not modify the collection, it actually creates a new one.
The extension method you wrote creates this new collection and then discards it. The assignment is redundant: you’re assigning to a local parameter, which goes out of scope immediately after.
When you’re calling the method, you’re also discarding its result instead of assigning it back.
Therefore, you should write the method like this:
public static List<string> ReplaceStringInListWithAnother(
this List<string> my_list, string to_replace, string replace_with)
{
return (from s in my_list select
(s == to_replace ? replace_with : s)).ToList();
}
and the call like this:
strings = strings.ReplaceStringInListWithAnother("delete", "");
By the way, you can make the function more useful by making it generic:
public static List<T> ReplaceInList<T>(this List<T> my_list,
T to_replace, T replace_with) where T : IEquatable<T>
{
return (from s in my_list select
(s.Equals(to_replace) ? replace_with : s)).ToList();
}
This way you can use it for other things, not just strings. Furthermore, you can also declare it to use IEnumerable<T> instead of List<T>:
public static IEnumerable<T> ReplaceItems<T>(this IEnumerable<T> my_list,
T to_replace, T replace_with) where T : IEquatable<T>
{
return from s in my_list select (s.Equals(to_replace) ? replace_with : s);
}
This way you can use it for any collection of equatable items, not just List<T>. Notice that List<T> implements IEnumerable<T>, so you can still pass a List into this function. If you want a list out, simply call .ToList() after the call to this one.
Update: If you actually want to replace elements in a list instead of creating a new one, you can still do that with an extension method, and it can still be generic, but you can’t use Linq and you can’t use IEnumerable<T>:
public static void ReplaceInList<T>(this List<T> my_list,
T to_replace, T replace_with) where T : IEquatable<T>
{
for (int i = 0; i < my_list.Count; i++)
if (my_list[i].Equals(to_replace))
my_list[i] = replace_with;
}
This will not return the new list, but instead modify the old one, so it has a void return type like your original.
Here's a hint: what do you expect the below code to do?
void SetToTen(int y)
{
y = 10;
}
int x = 0;
SetToTen(x);
Hopefully, you understand that the SetToTen method above does nothing meaningful, since it only changes the value of its own local variable y and has no effect on the variable whose value was passed to it (in order for that to happen, the y parameter would have to be of type ref int and the method would be called as SetToTen(ref x)).
Keeping in mind that extension methods are really just static methods in fancy clothes, it should be clear why your ReplaceStringInListWithAnother is not doing what you expected: it is only setting its local my_list variable to a new value, having no effect on the original List<string> passed to the method.
Now, it's worth mentioning that the only reason this is not working for you is that your code works by setting a variable to a new object*. If you were to modify the List<string> passed to ReplaceStringInListWithAnother, everything would work just fine:
public static void ReplaceStringInListWithAnother( this List<string> my_list, string to_replace, string replace_with)
{
for (int i = 0; i < my_list.Count; ++i)
{
if (my_list[i] == to_replace)
{
my_list[i] = replace_with;
}
}
}
It's also worth mentioning that List<string> is an overly restrictive parameter type for this method; you could achieve the same functionality for any type implementing IList<string> (and so I'd change the my_list parameter to be of type IList<string>).
*Reading your question again, it seems clear to me that this is the main point of confusion for you. The important thing you have to realize is that by default, everything in C# is passed by value. With value types (anything defined as a struct -- int, double, DateTime, and many more), the thing that's passed is the value itself. With reference types (anything that's defined as a class), the thing that's passed is a reference to an object. In the latter case, all method calls on references to objects of mutable types do actually affect the underlying object, since multiple variables of reference type can point to the same object. But assignment is different from a method call; if you assign a reference to an object that has been passed by value to some new reference to an object, you are doing nothing to the underlying object, and therefore nothing is happening that would be reflected by the original reference.
This is a really important concept that many .NET developers struggle with. But it's also a topic that's been explained thoroughly elsewhere. If you need more explanation, let me know and I'll try to dig up a link to a page that makes all of this as clear as possible.
You haven't shown the code for "Pop" so it's hard to know what you mean. You talk about "when I return the collection" but you're not returning anything - the method has a void return type.
LINQ typically doesn't change the contents of an existing collection. Usually you should return a new collection from the extension method. For example:
public static IEnumerable<string> ReplaceAll
(this IEnumerable<string> myList, string toReplace, string replaceWith)
{
return toReplace.Select(x => x == toReplace ? replaceWith : x);
}
(I've made it more general here - you shouldn't start materializing lists unless you really need to.)
You'd then call it with:
strings = strings.ReplaceAll("delete", "").ToList();
... or change the type of string to IEnumerable<string> and just use
strings = strings.ReplaceAll("delete", "");

C# passing parameters by reference

I have the below piece of code which Prefixs a string to the start of each member of a string array. ie. ["a","b","c"] prefixed with "z" becomes ["za","zb","zc"].
private string[] Prefix(string[] a, string b) {
for(int i = 0;i < a.Length;i++) {
a[i] = b + a[i];
}
return a;
}
The function works fine (although if theres a better way to do this, I'm happy to hear it), but I'm having issues when passing parameters.
string[] s1 = new string[] {"a","b"};
string[] s2 = Prefix(s1,"z");
Now as far as I can tell, I'm passing s1 by Value. But when the Prefix function has finished, s2 and s1 have the same value of ["za,"zb"], or s1 has been passed by reference. I was certain you had to explicitly declare this behaviour in c#, and am very confused.
As others have said, the reference is passed by value. That means your s1 reference is copied to a, but they both still refer to the same object in memory. What I would to do fix your code is write it like this:
private IEnumerable<string> Prefix(IEnumerable<string> a, string b) {
return a.Select(s => b + s);
}
.
string[] s1 = new string[] {"a","b"};
string[] s2 = Prefix(s1,"z").ToArray();
This not only fixes your problem, but also allows you to work with Lists and other string collections in addition to simple arrays.
C#, like Java before it, passes everything by value by default.
However, for reference types, that value is a reference. Note that both string and array are reference types.
Here's a better way to do it:
private string[] Prefix(string[] a, string b) {
return a.Select(s => b + s).ToArray();
}
or even:
private IEnumerable<string> Prefix(IEnumerable<string> a, string b) {
return a.Select(s => b + s);
}
You are passing the reference to s1 by value. In other words, your a parameter (when in the Prefix function scope), and your s1 variable, are references to the same array.
strings are immutable
this means that when you append a string to a string you get out a totally new string - its for performance reasons. its cheaper to make a new string that to reallocate the existing array.
hence why it feels like you are working with strings by value
in c# all reference types are passed by reference by default - ie classes creaped on the heap rather than values.
Actually, a reference to s1 was passed by value to Prefix(). While you now have two different references, both s1 and s2 still refer to the same string array, as arrays are reference types in C#.
Passing by value doesn't mean that you can't change things.
Objects are passed by value, but the value is (effectively) a pointer to the object.
Arrays are objects and a pointer to the array gets passed in. If you change the contents of the array in a method, the array will reflect the changes.
This doesn't happen with strings only because strings are immutable - once constructed, their contents can't change.
A reference to the string array is passed by value.
Consequently, the original array reference in the calling method cannot be changed, meaning that a = new string[10] within the Prefix method would have no impact on the s1 reference in the calling method. But the array itself is mutable, and a duplicate reference to the same array is capable of making changes to it in a way that would be visible to any other reference to the same array.
The value of an object is passed. For "reference" objects this is the value of the reference. A clone/copy/duplicate of the underlying data is not made.
To fix the issue observed, simply don't mutate the input array -- instead, create a new output array/object and fill it in appropriately. (If you must use arrays, I would likely use this approach as it's so boringly C-like anyway.)
Alternately, you can clone the input array first (which also creates a new object). Using a clone (which is a shallow copy) in this case is okay because the inner members (strings) are themselves immutable -- even though they are reference types the value of the underlying object can't be changed once created. For nested mutable types, more care may need to be taken.
Here are two methods which can be used to create a shallow copy:
string[] B = (string[])A.Clone();
string[] B = (new List<string>(A)).ToArray();
It's not inclusive.
Personally though, I would use LINQ. Here is a teaser:
return a.Select(x => b + x).ToArray();

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

Why is params 'less performant' than a regular array?

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.

Categories

Resources