I have the following situation:
A constructor takes 6 values.
Some of them have default values, some not.
#pseudocode# Foo(int a, int b=2, int c=3, int d=4, int e=5, int f){}
And I want to be able to call all possible combinations without having to write always all 6 parameters.
#pseudocode# Foo f1 = new Foo(a=1, d=7, f=6);
#pseudocode# Foo f2 = new Foo(a=1, b=9, d=7, f=6);
Besides doing this with method overloading (which would be tedious), is there a more elegant solution?
in C# 4, there are named parameters see Named and Optional Arguments (C# Programming Guide)
which would result in
new Foo(a: 1, d: 7, f: 6);
Another solution wwould be to define a Constructor with your defaut value ans use Object Initializer to set the values How to: Initialize Objects by Using an Object Initializer (C# Programming Guide)
new Foo()
{
a = 1,
d = 7,
f = 6
};
use the following for naming arguments:
Foo f1 = new Foo(a: 1, d: 7, f: 6);
Foo f2 = new Foo(a: 1, b: 9, d: 7, f: 6);
More information on Named and Optional Arguments avalable here:
http://msdn.microsoft.com/en-us/library/dd264739.aspx#Y515
I would prefer refactoring to a Parameter Object. Something like:
Foo f1 = new Foo (new FooParameters () { B = 7 })
And your FooParamaters class can encapsulate the defaults:
public class FooParameters
{
public int A { get; set; }
public int B { get; set; }
public FooParameters ()
{
A = 1;
B = 2;
}
}
2 things:
1) default parameters are left to right, once you start defaulting parameters you cannot have a non-defaulted after it, so f must have a default (in your example)
2) you can use parameter naming to skip over a default parameter:
var f1 = new Foo(5, 9, e: 9, f: 10);
This gives a =5, b = 9, c = the default, d = default, e = default, f = 10
Provided you are using C# 4.0 compiler of course...
Related
I'm making a C# script in Unity. My intention is to create a class Scenario, create classes representing different scenarios, which would then be stored in an array scenarioListAll.
A (simplified) version of the code is as follows:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class OverallManager2 : MonoBehaviour
{
public static object[] scenarioListAll = new object[40];
public class Scenario
{
public string scenarioDesc;
public bool surprise; // The 'surprise' bool I want to reference is defined here
public string surpriseType;
public int[] leftOption;
public int[] rightOption;
public int scenarioNumber;
public Scenario(string st, bool sp, int[] l, int[] r, int n)
{
scenarioDesc = st;
surprise = sp;
leftOption = l;
rightOption = r;
scenarioNumber = n;
}
// I haven't used this, but I'm not sure if this matters so I'm including this too
public Scenario(string st, bool sp, string spt, int[] l, int[] r, int n)
{
scenarioDesc = st;
surprise = sp;
surpriseType = spt;
leftOption = l;
rightOption = r;
scenarioNumber = n;
}
}
public static int[] getArray(int a, int b, int c, int d, int e, int f)
{
int[] arr = new int[6] {a, b, c, d, e, f};
return arr;
}
// Storing scenarios, am looking for the bool (2nd position)
public Scenario s1 = new Scenario("Test1", false, getArray(1, 1, 1, 1, 1, 1), getArray(1, 1, 1, 1, 1, 1), 1);
public Scenario s2 = new Scenario("Test2", true, getArray(1, 1, 1, 1, 1, 1), getArray(1, 1, 1, 1, 1, 1), 2);
public Scenario s3 = new Scenario("Test3", false, getArray(1, 1, 1, 1, 1, 1), getArray(1, 1, 1, 1, 1, 1), 3);
void Awake()
{
// Store scenarios in object array
scenarioListAll[0] = s1;
scenarioListAll[1] = s2;
scenarioListAll[2] = s3;
for(int i = 0; i < 40; i++)
{
object temp = scenarioListAll[i]; // Trying to extract the object stored in the array in a temp object
bool surpriseCheck = temp.surprise; // I am having problems with this line
if(surpriseCheck == true)
{
// Do something
}
}
}
// Ignoring start and update since they're irrelevant in this context
}
What I would like to do is to check whether the surprise element within a newly defined scenario (e.g. s1) is true. To do that, I was planning to extract the scenario stored in the array scenarioListAll, and then extract the surprise component from there. However, I'm couldn't figure out how to do this (e.g. in the code shown above, it returns Compiler Error CS1061).
I don't think I was able to find any documentation on this either, but I might not have understood something. I'm learning on my own so please bear with my poor knowledge/presentation.
Thank you for your time. Your help is much appreciated.
You are having a compilation issue due to the fact that the c# compiler doesn't know that temp is a Scenario since you declared it as "object". If you want to loop through the scenarios and check to see if they are a surprise you can use something like this:
foreach(Scenario temp in scenarioListAll)
{
bool surpriseCheck = temp.surprise;
if(surpriseCheck == true)
{
// Do something
}
}
Another way of accomplishing the same task with more control over the iteration would be:
for(int i = 0; i < scenarioListAll.Length; i++)
{
Scenario temp = scenarioListAll[i];
bool surpriseCheck = temp.surprise;
if(surpriseCheck == true)
{
// Do something
}
}
The benefit of the first version is that you don't have to worry about overrunning the bounds of the array. As Mike added below you could also use var to have the compiler fill in the type for you.
It's sometimes easiest to allow the compiler to determine the type of a variable for us.
In your case, you've specified that the variable temp will be of type object. Now, that's fine, but while a Scenario derives from object, and object is the lowest level class in the .Net environment, and is not a Scenario.
The var keyword doesn't mean that the declared type is of a "variable" type, instead it's telling the compiler just to "fill in" the correct type, based on the action you're taking. So, to put this in to action in your case, you could do this instead:
for( var i = 0; i < 40; i++ ) // notice the use of var here as well
{
var scenario = scenarioListAll[i]; // renamed temp to scenario
// var surpriseCheck = scenario .surprise; // var used but line not required
if( scenario.surprise )
{
// Do something
}
}
I went overboard there to demonstrate that the compiler is quite happy with the var keyword just about wherever you'd specify a data type as a type for a variable. Obviously not when you're trying to cast types, and there ARE sometimes where you'd want to specify the exact type you're trying to instantiate.
In your case, your next issue will be that you've defined the array as having 40 object elements, but you've then only instantiated 3 Scenario elements (which is valid, but probably not quite what you want overall). So your code, as it stands, is going to NullReference error out. You'll be able to avoid that, with a small modification, so your amended code could look like this, to include some type checks:
for( var i = 0; i < scenarioListAll.Length; i++ )
{
// First, check to see if the object in the array cell is a Scenario.
// If the item in the cell is a Scenario, check to see if surprise is set.
if ( scenarioListAll[i] is Scenario scenario && scenario.surprise )
{
// Do something
}
}
for more information on the is keyword, check the Microsoft Docs here.
Is there some pattern/syntaxis/library for C# to accomplish something like the delegate helper method that exists on Ruby on Rails (https://apidock.com/rails/Module/delegate)?
The problem is that if you google for delegates for C# you only find information about the delegate concept of C# that is slightly different from the concept in rails.
And continuing with the trip, is there an equivalent too for Javascript/Typescript?
There is no equivalent in c#, you need to implement it (create a facade method) manually in each case.
An example from the link:
class Foo
CONSTANT_ARRAY = [0,1,2,3]
##class_array = [4,5,6,7]
def initialize
#instance_array = [8,9,10,11]
end
delegate :sum, to: :CONSTANT_ARRAY
delegate :min, to: :##class_array
delegate :max, to: :#instance_array
end
Foo.new.sum # => 6
Foo.new.min # => 4
Foo.new.max # => 11
Could be translated to c# as:
public class Foo
{
public static readonly IReadOnlyList<int> CONSTANT_ARRAY = new[] {0, 1, 2, 3};
public static int[] class_array = {4, 5, 6, 7};
public int[] instance_array = {8, 9, 10, 11};
public int sum() => CONSTANT_ARRAY.Sum();
public int min() => class_array.Min();
public int max() => this.instance_array.Max();
}
...
new Foo().sum(); // => 6
new Foo().min(); // => 4
new Foo().max(); // => 11
I saw NRE when using array initializer in object initializer and there was a update and https://github.com/dotnet/roslyn/wiki/New-Language-Features-in-C%23-6#extension-add-methods-in-collection-initializers but still I cannot understand.
var arr = new int[] { 1, 2, 3 } generate IL code using stelem.
But int[] arr = { 1, 2, 3 } generate IL code using RuntimeHelpers.InitializeArray. I think it is using Array.Add extension method which was talk in that answer.
But in object initializer, all array initializer generate second code.
An example,
new A() {
arr = new int[] { 1, 2, 3 }
}
Array arr is created using RuntimeHelpers.InitializingArray too. Then, doesn't it mean there isn't any problem in next code?
new A() {
arr = { 1, 2, 3 } // Compiler error!
}
Not like old version of c# compiler, it makes compiler error saying system.array does not contain a definition for Add. What's happening?
EDIT
I thought just syntax without new [] makes differences but actually more than three elements makes different IL code.
The second syntax ( { arr = {... } }) is syntax sugar for sequence of value.arr.Add(.) - presumably value.arr is not initialized in your constructor and hence NRE.
Assuming A is:
class A {
public int[] arr {get;set}
}
then
var x = new A() {
arr = { 1, 2, 3 } // Compiler error!
};
is the same as
var x = new A(); // note x.arr is default null here
x.arr.Add(1); // NRE is arr is list, can't compile for int[]
x.arr.Add(2);
x.arr.Add(3);
Fix: use list, there is no reasonable way to add elements to an array.
If using some other type that does not have .Add - implement extension method that is visible to that code.
Why version with new works:
var x = new A() {
arr = new int[] { 1, 2, 3 }
};
is equivalent* of
var x = new A();
x.arr = new int[] { 1, 2, 3 };
Note that in array initializer you can use both syntaxes to the same effect, but not in array field's initializers (All possible C# array initialization syntaxes)
int[] x = { 10, 20, 30 }; // valid, same as int[] x = new int[]{ 10, 20, 30 };
while new A { arr = { 10, 20, 30} } is not the same as new A { arr = new int[3]{ 10, 20, 30} }
*It is really a bit more complicated to satisfy rules when change must be observable - see Eric Lippert's comment below
In an expression context these are legal array values:
new int[3] { 10, 20, 30 }
new int[] { 10, 20, 30 }
new[] { 10, 20, 30 }
In a local or member variable initializer context, this is a legal array initializer:
int[] x = { 10, 20, 30 };
And this is a legal collection initializer:
List<int> x = new List<int> { 10, 20, 30 };
And, if X<T> is IEnumerable<T> and has a method Add(T, T, T), this is legal:
X<int> x = new X<int> { { 10, 20, 30}, {40, 50, 60} };
But in a member or collection initializer context this is not a legal array property initializer:
new A() {
arr = { 10, 20, 30 }
}
(Note that I summarize and comment on the rules for arrays here https://stackoverflow.com/a/5678393/88656)
The question, as I understand it, is "why not?"
The answer is "no good reason". It's simply an oddity of the C# grammar and the rules for object and collection initializers.
I many times considered fixing this oddity but there was always something better to do with my time; it's a fix that benefits basically no one because the workaround is so easy.
I conjecture that there's nothing stopping the C# team from designing, specifying, implementing, testing and shipping that feature aside from the fact that there are still about a million other features that would be a better use of their time.
If you feel strongly about it, well, the compiler is open source; feel free to propose the feature and advocate for it. Or, for that matter, implement it and submit a pull request. (After you propose the feature.)
Until the feature you want is implemented you'll just have to use one of the three "expression" forms listed above. It is not burdensome to do so.
Consider the following code snippet to follow my mind chronologically. The commented statements cannot be compiled.
var data1 = new int[3] { 1, 2, 3 };
var data2 = new int[] { 1, 2, 3 };
var data3 = new[] { 1, 2, 3 };
var data4 = new[] { 1, 2, 3.0f };
The simplification done for data3 and data4 is understandable.
int[] data5 = { 1, 2, 3 };
//var data6 = { 1, 2, 3 };
Unable to infer the declaration for data6 is understandable.
var data7 = new int[] { };
//var data8 = new [] { };
//int[] data9 = new [] { };
Unable to infer the declaration for data8 is also understandable.
What I don't understand is why data9 which is more informative cannot be compiled while data10 that is less informative can be compiled.
int[] data10 = { };
//var data11 = { };
Declaring data11 that cannot be compiled is also understandable.
The cases where there is a new keyword present, are usual expressions that can be used by themselves in any context where an expression is required. You use them for declarations with assignment, but they can be used in other contexts, for example:
return new[] { 3 };
or:
Call(new[] { 3 });
and so on. In these new array expressions, the type must be clear from the expression itself. This is required even when there is a variable declaration on the left.
The case data9 therefore, with int[] data9 = new [] { }; is illegal because the expression new[] { } is illegal. It is somewhat like:
object related9 = someBoolean ? "Yes" : new Exception();
which is also illegal because the expression someBoolean ? "Yes" : new Exception() is illegal in itself, with incompatible types. There is a declaration of type object, but that does not make the right-hand side legal.
The examples data5 and data10 show a completely unrelated syntax for array variable declaration.
int[] data5 = { 1, 2, 3 };
Here the right-hand side of the = operator is not an expression in itself. This syntax requires a declaration with explicit type on the left of the = sign (so data6 is not OK). This syntax is related to object initializers and collection initializers and was introduced in the C# language together with them, whereas the new[] { ... } syntax is a bit older.
You should see this answer by Lippert, then read the official C# Language Specification.
C# is of type of language that cannot infer types from context. It means that it can extract types only from the expression outside, not vice versa.
In other words, the phenomenon you have shown is identical to the fact that the compiler cannot choose the correct overload in the following case:
int foo();
string foo();
string x = foo();
However it can choose the correct overload in the following case:
void bar(int x);
void bar(string x);
string x;
bar(x);
Edit: Servy commented that lambda expression is an exception for that rule. For example:
var a = new Func<int, bool>(a => a == 1);
the compiler cannot infer the type of "a => a == 1" without inferring it from the context.
This is my question:
Let's say I have a class with such a constructor:
Class A {
private int i;
public A(int new_i) {
i = new_i;
}
}
Can I initialize an array of A with an array of int?
If so, how can I do it?
I was thinking something like:
int[] b = new int[5] { 0,1,2,3,4};
A[] a;
a = new A[](b);
Thanks!
var a = Array.ConvertAll(b, item => new A(item));
The advantage here is that it will be created with the right size, without any intermediate steps - plus it works without LINQ.
In C# 2.0 it is a bit uglier - the following is identical:
A[] a = Array.ConvertAll<int,A>(b,
delegate(int item) { return new A(item); });
The combination of var, lambdas, and improved generic type inference really shine for C# 3.0 here.
You could use LINQ:
int[] b = new int[5] { 0, 1, 2, 3, 4 };
A[] a = b.Select(x => new A(x)).ToArray();