Having the code below, why is the variable declaration considered as correct syntax, but not also the method call?
public static void Func(string[] p)
{
}
public static void Test()
{
string[] a = { "x", "y", "z" };
Func({"x", "y", "z"});
}
It looks like everybody else is focusing on the workaround (which is simply to specify that you need a new [] { "some", "strings" }. The why is a little less obvious though.
The former is valid because the compiler knows to use your initialization syntax to create an Array (because you've defined it as such).
The later would have some issues. It may seem trivial in this case because the compiler should, theoretically, be able to figure out that you need a string[]. Think about cases where you have a Func<IEnumerable<string>> though. What type gets generated in that case? Would the compiler take a wild-ass guess? Always use an Array (even though there might be a better fit)? It could be one of a number of possibilities.
I'm guessing that's the reason that the language specification doesn't allow for passing things this way.
You need to pass in a value as the argument. {"x", "y", "z"} is not a value. It can be used as short-hand for initializing a variable.
Note that both of these are valid:
List<string> a = new List<string>() {"x", "y", "z"};
string[] b = new string[] {"x", "y", "z"};
And the full version of what it represents:
List<string> a = new List<string>();
a.Add("x");
a.Add("y");
a.Add("z");
So you need to make an object (new)
new [] {"x", "y", "z"}
Or make an object beforehand and pass that in.
As for why this is like this, you need to pass in a value, not a helper for array initialization.
You can initialize the object directly on the inside of the method call, but do not think a good practice.
where you used:
Func ({"x", "y", "z"});
The above form you tried to pass an object not instantiated, ie it does not already exist on your "context". The correct is you initialize it to be generated in a reference memory to this value and thus the "Func" method you can use it.
In the case of variable declaration:
string[] a = {"x", "y", "z"};
In this case your compiler is reading:
string[] a = new string[] {"x", "y", "z"};
It does this interpretation alone, without requiring you to do object initialization explicitly.
So my answer is best for this situation you must first create the object then pass it as a parameter to the method:
string[] a = {"x", "y", "z"};
Func (s);
Related
I've seen All possible C# array initialization syntaxes which shows several different ways to initialize an array.
Does the following initialization creates an instance that is different in some ways compared to one created with regular call with new ?
Initialization:
string[] strArray = {"one","two","three"};
Compared to string[] strArray = new String[] {"one","two","three"};
It is a syntactic sugar. The compiler transforms this:
string[] strArray = {"one","two","three"};
To This:
string[] expr_07 = new string[] {
"one",
"two",
"three"
};
Above output is using Roslyn C# compiler.
You can't initialize something that's not there. So yes, a new instance does get created, in the exact same way as though you had written the new operator. It's just a convenience provided by the language for when you're initializing an array at the same time that you're declaring it so you aren't forced to write it out (when doing it in a separate statement, the new operator is required because the compiler can't assume you haven't already assigned an array reference to the variable beforehand — the very bare minimum you can get away with in a separate statement is strArray = new[] {...};).
Case
This morning I refactored some Logging method and needed to change a method's 'params' parameter in a normal array. Consequently, the call to the method had to change with an array parameter. I'd like the method call to change as less as possible, since it's a heavily used utility method.
I assumed I should be able to use the collection initializer to call the method, but it gave me a compile-error. See the second call in the example below. The third call would be fine too, but also results in an error.
Example
void Main()
{
// This works.
object[] t1 = { 1, "A", 2d };
Test(t1);
// This does not work. Syntax error: Invalid expression term '{'.
Test({1, "A", 2d });
// This does not work. Syntax error: No best type found for implicitly-typed array.
Test(new[] { 1, "A", 2d });
// This works.
Test(new object[] { 1, "A", 2d });
}
void Test(object[] test)
{
Console.WriteLine(test);
}
Question
Is there any way to call Test() without initializing an array first?
The problem is that C# is trying infer the type of the array. However, you provided values of different types and thus C# cannot infer the type. Either ensures that all you values are of the same type, or explicitly state the type when you initialize the array
var first = new []{"string", "string2", "string3"};
var second = new object[]{0.0, 0, "string"};
Once you stop using params there is no way back. You will be forced to initialize an array.
Alternative continue using params:
public void Test([CallerMemberName]string callerMemberName = null, params object[] test2){}
List<string> a = new List<string>() { "a", "b", "c" };
List<string> b = new List<string>() { "a", "b", "c", "d", "e", "f" };
b.RemoveAll(a.Contains);
If you loop through b it will now only contain d e and f. Can anyone expand out whats actually happening, because currently it doesnt make any sense at all.
Edit: I was more talking about the use of predicates. How does know how to pass what into where?
b.RemoveAll(<function that takes a string and returns true if we want to remove it>)
no lambda expression required.
perhaps you'd like for the line to read
b.RemoveAll(x => a.Contains(x))
however, the function x=> a.Contains(x) is simply a function that takes a string and returns a bool indicating whether a contains x. a.Contains is already a function that does that.
The { } Syntax is a collection initializer. The code is equivalent to
List<string> a = new List<string>();
a.Add("a");
a.Add("b");
a.Add("c");
List<string> b = new List<string>();
b.Add("a");
b.Add("b");
b.Add("c");
b.Add("d");
b.Add("e");
b.Add("f");
b.RemoveAll is a function that calls another function and passes in a string. It's like this:
foreach(string s in b) {
if(FunctionToCall(s) == true) b.Remove(s);
}
a.Contains is a function that takes a string and returns a bool. So the code can be changed to:
foreach(string s in b) {
if(a.Contains(s)) b.Remove(s);
}
Note that in this Lambda-Syntax, you are passing the "a.Contains" function - not the result of the function! It's like a function pointer. RemoveAll expects to take a function in the form of "bool FunctionName(string input)".
Edit: Do you know what delegates are? They are a bit like function pointers: A delegate specifies a signature ("Takes 2 strings, returns an int"), and then you can use it like a variable. If you don't know about delegates, read Karl Seguins article.
Some delegates are needed extremely often, so the .net Framework developers added three types of extremely common delegates:
Predicate: A delegate that takes a T and returns a bool.
Action: A delegate that takes 1 to 4 parameters and returns void
Function: A delegate that takes 0 to 4 parameters and returns a T
(Shamelessly copied from Jon Skeet's Answer here)
So predicate is just the name given for a delegate, so that you don't have to specify it yourself.
If you have ANY function in your assembly with the signature
"bool YourFunction(string something)", it is a Predicate<string> and can be passed into any other function that takes one:
public bool SomeFunctionUsedAsPredicate(string someInput)
{
// Perform some very specific functionality, i.e. calling a web
// service to look up stuff in a database and decide if someInput is good
return true;
}
// This Function is very generic - it does not know how to check if someInput
// is good, but it knows what to do once it has found out if someInput is good or not
public string SomeVeryGenericFunction(string someInput, Predicate<string> someDelegate)
{
if (someDelegate.Invoke(someInput) == true)
{
return "Yup, that's true!";
}
else
{
return "Nope, that was false!";
}
}
public void YourCallingFunction()
{
string result = SomeVeryGenericFunction("bla", SomeFunctionUsedAsPredicate);
}
The whole point is separation of concerns (see the comment in SomeGenericFunction) and also to have very generic functions. Look at my generic, extensible string encoding function. This uses the Func rather than the Predicate delegate, but the purpose is the same.
look at it like this:
foreach(string s in b)
{
if(a.Contains(s))
b.Remove(s);
}
You passing the bit in the if evaluation clause as a delegate (managed equivalent of a function pointer). The RemoveAll method unrolls the list and does the rest.
It says 'remove all elements in b that are contained in a'. So you're left only with the one's in b that weren't also present in a.
Here is a slightly expanded version of your code that shows what's happening:
List<string> a = new List<string> () { "a", "b", "c" };
List<string> b = new List<string> () { "a", "b", "c", "d", "e", "f" };
Predicate<string> ps = a.Contains;
b.RemoveAll (ps);
For every element in b, a.Contains() is being evaluated for that element. If it's true, it is removed.
So, you're removing every element from b that is also contained in a.
The function "a.Contains" is being passed as an argument to RemoveAll.
The signature for RemoveAll looks like this ...
public int RemoveAll(Predicate<T> match);
A Predicate<T> is a delegate that takes Ts and returns bools ...
public delegate bool Predicate<T>(T obj)
RemoveAll is thus asking for a reference to a method that will, in your case, take strings and return bools. List<T>.Contains is such a method. You will note that the signature for List<T>.Contains matches the Predicate delegate (it takes Ts and returns bools) ...
public bool Contains(T item);
RemoveAll will apply whatever Predicate is passed as "match" to each element of the list upon which it is called (b in your case). So, if a.Contains("a"), for example, returns true, then all the a's will be removed from the b list. Thus, in your example, all the a's, b's and c's are removed.
Using a tool like .NET Reflector will let you look at the code of RemoveAll and that might help clarify what's happening under the covers.
Without initialization how is it possible to assign values to arrays?
string[] s={"all","in","all"};
I mean why did not the compile show error?.Normally we need to
initialize ,before assign values.
It's just syntactic sugar.
This:
string[] s = {"all","in","all"};
is compiled to the same code as:
string[] tmp = new string[3];
tmp[0] = "all";
tmp[1] = "in";
tmp[2] = "all";
string[] s = tmp;
Note that the array reference is not assigned to s until all the elements have been assigned. That isn't important in this particular case where we're declaring a new variable, but it would make a different in this situation:
string[] s = { "first", "second" };
s = new string[] { s[1], s[0] };
The same is true for object and collection initializers - the variable is only assigned at the end.
It is possible to declare an array variable without initialization.
Check this out
http://msdn.microsoft.com/en-us/library/0a7fscd0%28VS.71%29.aspx
You aren't "assigning a value to array". You are initializing a variable of type "reference to array". The value with which you initialize it is a reference to an array which was created by the use of short array initializer syntax {...}. While it is only permissible in initializer of variables of array type, it is exactly equivalent to new T[] { ... }, where T is deduced from type of variable.
I think you want to know why
string[] s={"all","in","all"};
works when you would expect to be required to initialize the array first like this :
string[] s = new string[];
or
string[] s = new string[] {"all","in","all"};
The answer is just compiler magic. The compiler knows based on the initialization how big to make the array so it just does it behind the scenes for you. Much like the var keyword, the point is to limit the amount of redundant information you're required to type.
The {"all","in","all"} part is the initialization. the new string[] part can be omitted because the curly braces and string are short hand notation. Take a look at MSDN on Single Dimension Arrays.
string[] s = new string[] { "all","in","all"};
and its shorthand version
string[] s = {"all","in","all"};
are the same thing. See MSDN (Initializing Arrays section) for more detail.
You don't need the new string[] part in C#3 or higher - this works fine
string[] s = { "all","in","all"};
It's just a case of the compiler being a bit smarter and working out what you mean - the back end IL will be the same.
You can do so simply because it is allowed, doing so in two steps is not necessary so this is the shorthand. Consider it sugar.
I've always wanted to be able to use the line below but the C# compiler won't let me. To me it seems obvious and unambiguos as to what I want.
myString.Trim({'[', ']'});
I can acheive my goal using:
myString.Trim(new char[]{'[', ']'});
So I don't die wondering is there any other way to do it that is closer to the first approach?
The string.Trim(...) method actually takes a params argument, so, why do you not just call:
myString.Trim('[', ']');
Others have concentrated on the specific example (and using the fact that it's a parameter array is the way to go), but you may be interested in C# 3's implicit typing. You could have written:
myString.Trim(new[] {'[', ']'});
Not quite as compact as you were after, as you still need to express the concept of "I want to create an array" unless you're writing a variable initializer, but the type of the array is inferred from the contents.
The big use case for this is anonymous types:
var skeets = new[] {
new { Name="Jon", Age=32 },
new { Name="Holly", Age=33 },
new { Name="Tom", Age=5 },
new { Name="Robin", Age=3 },
new { Name="William", Age=3 }
};
Here you couldn't write the name of the type, because it doesn't have a name (that's expressible in C#).
One other point to make about your specific example - if you're going to use this frequently (i.e. call the Trim method often) you may want to avoid creating a new array each time anyway:
private static readonly char[] SquareBrackets = {'[', ']'};
public void Whatever() {
...
foo = myString.Trim(SquareBrackets);
...
}
This will work too ...
myString.Trim( '[',']' );
Note the params declaration in the defition of Trim, it let's you pass as many arguments as you want and takes them as an array.
You could also try this:
myString.Trim("[]".ToCharArray());