9.1 Optional and named parameters
class Program
{
private static int s_n = 0;
private static void M (int x = 9, string s = "A", DateTime dt = default (DateTime), Guid guid = new Guid ())
{
Console.WriteLine ("x = {0}, s = {1}, dt = {2}, guid = {3}", x, s, dt, guid);
}
public static void Main ()
{
// 1. Equivalent to M (9, "A", default (DateTime), new Guid ());
M ();
// 2. Equivalent to M (8, "X", default (DateTime), new Guid ());
M (8, "X");
// 3. Equivalent to M (5, "A", DateTime.Now, Guid.NewGuid ());
M (5, guid: Guid.NewGuid (), dt: DateTime.Now);
// 4. Equivalent to M (0, "1", default (DateTime), new Guid ());
M (s_n ++, s_n ++. ToString ());
// 5. Equivalent to the following two lines of code:
// string t1 = "2"; int t2 = 3;
// M (t2, t1, default (DateTime), new Guid ());
M (s: (s_n ++). ToString (), x: s_n ++);
}
}
9.1.1 Rules and principles
Default values can be specified for parameters of methods, constructor methods, and parameterized attributes (C # indexers). You can also specify default values for parameters that are part of the delegation definition.
Parameters with default values must be placed after all parameters without default values. There is one exception: "parameter array" parameters must be placed after all parameters (including those with default values), and the array itself cannot have a default value.
The default value must be a constant value that can be determined at compile time (including primitive types, enumerated types, and any reference types that can be set to null). Value type parameters can set the default value as an instance of the value type and have all its fields contain zero values. The default or new keywords can be used to express this meaning, the IL code generated by the two grammars is completely the same.
Do not rename parameter variables, otherwise any caller will pass the actual parameters by parameter name, and their code must also be modified.
If the method is called from outside the module, changing the default value of the parameter is potentially dangerous. The call site (where the call is made) embeds the default value in its call. If the default value of the parameter is changed later, but the code containing the call site is not recompiled, it will pass the old default value when calling your method. Consider using the default value of 0 / null as the sentinel value to indicate the default behavior. example:
//Do not do this:
private static string MakePath (string filename = "Untitled")
{
return string.Format (@ "c \ {0} .txt", filename);
}
// To do this:
private static string MakePath (string filename = null)
{
return string.Format (@ "C: \ {0} .txt", filename ?? "Untitled");
}
If the parameter is identified with the ref or out keyword, the default value cannot be set.
When calling methods with optional or named parameters, also pay attention to the following additional rules and principles:
Arguments can be passed in any order, but named arguments can only appear at the end of the argument list.
You can pass actual parameters by name to parameters that have no default value, but all required actual parameters must be passed (either by location or by name).
C # does not allow to omit actual parameters between commas, such as M (1,, DateTime.Now). For parameters with default values, if you want to omit their actual parameters, just pass the actual parameters by way of the parameter name.
If the parameter requires ref / out, in order to pass the actual parameter by parameter name, please use the following syntax:
// Method declaration:
private static void M (ref int x) {...}
// Method call:
int a = 5;
M (x: ref a);
When C # calls a COM component, if the actual parameters are passed by reference, C # also allows ref / out to be omitted, which further simplifies coding. But if the call is not a COM component, C # requires that the ref / out keyword must be applied to the actual parameter.
9.1.2 DefaultParameterValueAttribute and OptionalAttribute
9.2 Implicitly typed local variables
You cannot use var to declare the parameter type of a method.
You cannot use var to declare fields in a type.
Don't confuse dynamic and var. Declaring local variables with var is just a simplified syntax, which requires the compiler to infer specific data types based on expressions. The var keyword can only declare local variables inside a method, while the dynamic keyword applies to local variables, fields, and parameters. Expressions cannot be converted to var, but they can be converted to dynamic. Variables declared with var must be initialized explicitly, but variables declared with dynamic need not be initialized.
private static void ImplicitlyTypedLocalVariables ()
{
var name = "Jeff";
ShowVariableType (name); // Display: System.String
// var n = null; // Error, null cannot be assigned to implicitly typed local variables
var x = (String) null; // It can be written like this, but it has little meaning
ShowVariableType (x); // Display: System.String
var numbers = new int [] {1, 2, 3, 4};
ShowVariableType (numbers); // Display: System.Int32 []
// Complex types can type less
var collection = new Dictionary <String, Single> () {{"Grant", 4.0f}};
// Display: System.Collections.Generic.Dictionary`2 [System.String, System.Single]
ShowVariableType (collection);
foreach (var item in collection)
{
// Display: System.Collections.Generic.KeyValuePair`2 [System.String, System.Single]
ShowVariableType (item);
}
}
private static void ShowVariableType <T> (T t)
{
Console.WriteLine (typeof (T));
}
9.3 Passing parameters to methods by reference
The CLR defaults to passing all method parameters.
The CLR allows parameters to be passed by reference rather than by value. C # supports this feature with the keywords out or ref.
The CLR does not distinguish between out and ref, no matter which keyword is used, the same IL code will be generated. In addition, the metadata is almost identical except for one bit, which is used to record whether out or ref is specified when declaring a method.
The C # compiler treats these two keywords differently, and this difference determines which method is responsible for initializing the referenced object.
The method of marking parameters with out cannot read the value of the parameter, and must write to this value before returning. On the contrary, if the method is marked with ref, the value of the parameter must be initialized before the method is called, and the called method can read the value and / or write to the value;
Use out
public static void Main ()
{
int x; // x is not initialized
GetVal (out x); // x does not need to be initialized
Console.WriteLine (x); // Display "10"
}
private static void GetVal (out int v)
{
v = 10; // This method must initialize v
}
Use ref
public static void Main ()
{
int x = 5; // x has been initialized
GetVal (ref x); // x must be initialized
Console.WriteLine (x); // Display "15"
}
private static void GetVal (ref int v)
{
v + = 10; // This method can use the initialized value of v
}
You cannot define overloading methods that differ only in ref and out.
The variable passed to the method by reference must have the same type as declared in the method signature.
public static void Main ()
{
string s1 = "Jeffrey";
string s2 = "Richter";
//error! error! error!
// Swap (ref s1, ref s2);
// Variables passed by reference,
// Must match the expected method
object o1 = s1, o2 = s2;
Swap (ref o1, ref o2);
// Transform object to string after finishing
s1 = (string) o1;
s2 = (string) o2;
Console.WriteLine (s1); // Display "Richter"
Console.WriteLine (s2); // Display "Jeffrey"
}
private static void Swap (ref object a, ref object b)
{
object t = b;
b = a;
a = t;
}
Can use generics to correct the above method
public static void Main ()
{
string s1 = "Jeffrey";
string s2 = "Richter";
Swap (ref s1, ref s2);
Console.WriteLine (s1); // Display "Richter"
Console.WriteLine (s2); // Display "Jeffrey"
}
private static void Swap <T> (ref T a, ref T b)
{
T t = b;
b = a;
a = t;
}
9.4 Passing a variable number of parameters to the method
params can only be applied to the last parameter in the method signature.
This parameter can only identify a one-dimensional array (any type).
You can pass a null value for this parameter, or pass a reference to an array of zero elements.
Calling a method with a variable number of parameters has an impact on performance (unless you explicitly pass null). To reduce the impact on performance, consider defining several overloaded versions that do not use the params keyword, such as the Concat method of the System.String class.
public static void Main ()
{
Console.WriteLine (Add (new int [] {1, 2, 3, 4, 5})); // Display "15"
//or
Console.WriteLine (Add (1, 2, 3, 4, 5)); // Display "15"
// The following two lines show "0"
Console.WriteLine (Add ()); // Pass new int [0] to Add
Console.WriteLine (Add (null)); // pass null to Add: more efficient (because no array will be allocated)
}
private static int Add (params int [] values)
{
// Note: If you want, you can pass the values array to other methods
int sum = 0;
if (values! = null)
{
for (int x = 0; x <values.Length; x ++)
sum + = values [x];
}
return sum;
}
9.5 Design specifications for parameters and return types
When declaring the parameter type of a method, you should try to specify the weakest type, preferring an interface rather than a base class. For example, if you want to write a method to process a set of data items, it is better to declare the parameters with an interface (such as IEnumerable <T>) rather than a strong data type (such as List <T>) or a stronger interface type (such as ICollection <T> or IList <T>):
// Good: The method uses weak parameter types
public void ManipulateItems <T> (IEnumerable <T> collection) {}
// bad: the method uses strong parameter types
public void ManipulateItems <T> (List <T> collection) {}
On the contrary, it is generally best to declare the return type of the method as the strongest type (to prevent being restricted to a specific type). For example, the method preferably returns a FileStream instead of a Stream object:
// Good: The method uses a strong return class
public FileStream OpenFile () {}
// Not good: the method uses a weak return class
public Stream OpenFile () {}
If you want to maintain some flexibility and change what the method returns in the future, choose a weaker return type.
// Flexible: The method uses a weaker return type
public IList <string> GetStringCollection () {}
// Inflexible: the method uses a strong return type
public List <string> GetStringCollection () {}