Introduction to Generics
Introduction to Generics
Introduction to Generic Functions
Overview
So far, if we wanted to use different values to functions, we had to create a different function for each value. Here are examples:
// Use a string as parameter void ShowAString(string value) { } // Use an integer as parameter void ShowInteger(int value) { } // Use a double-precision value as parameter void ShowDouble(double value) { }
You can then call each function. Here are examples:
using static System.Console; // Calling a function that takes a string as argument ShowAString("Gertrude monay"); // Calling a function that takes a double-precision number as argument ShowDouble(28.73); // Calling a function that takes an integer as argument ShowInteger(55160); void ShowAString(string value) { WriteLine(string.Format("Employee Name: {0}", value)); } // Use a double-precision value as parameter void ShowDouble(double value) { WriteLine($"Hourly Salary: {value}"); } // Use an integer as parameter void ShowInteger(int value) { WriteLine("Yearly Salary: " + value.ToString()); }
This would produce:
Employee Name: Gertrude Monay Hourly Salary: 28.73 Yearly Salary: 55160 Press any key to close this window . . .
In the previous examples, we passed the values directly to the functions. If necessary, you can first declare and declare a variable, then pass that variable to the function.
Creating a Generic Function
In some cases, the C# language allows you to create one function but be able to call it with different types of values. A generic function is a function that can process a value whose type is known only when the value is accessed. To create a generic function, start with the return type of the function, a space, and a name. After the name of the function, type <>. Inside the <> operator, enter a letter or a name, which is referred to as parameter type. Here is an example:
void Show<TypeOfValue>()
{
}
One of the ways you can use the parameter type is to pass an argument to the function. You do this by preceding the name of the argument with the parameter type. Here is an example:
void Show<TypeOfValue>(TypeOfValue value)
{
}
In the body of the function, you can process the argument as you see fit. At a minimum, you can convert the argument to a string. Here is an example:
void Show<TypeOfValue>(TypeOfValue value)
{
return value.ToString();
}
By tradition, most programmers and most documents use the letter T for the parameter type. Here is an example:
public void Show<T>(T value) { return value.ToString(); }
Calling a Generic Function
One of the primary characteristics, and probably the most important characteristic, of a generic function is that, at the time it is defined, the function doesn't know the type of its parameter. This means that, when calling the function, you must make sure you clearly specify the type of value that will be processed. You can do this by passing the value that the function will process. Here are different examples of calling our Show() function:
using static System.Console; // Calling a function that takes a string as argument Show("Gertrude Monay"); // Calling a function that takes a double-precision number as argument Show(28.73); // Calling a function that takes an integer as argument Show(55160); // A generic function void Show<T>(T value) { WriteLine("Value: {0}", value); }
As an alternative, when calling the function, you can type the name of the function, followed by angle brackets. Inside the brackets, enter the data type of the value that will be processed. After the angle brackets, open the parentheses and, inside of them, type the value that will be processed. Here are examples:
using static System.Console; Show<string>("Gertrude Monay"); Show<double>(28.73); Show<int>(55160); void Show<T>(T value) { WriteLine("Value: {0}", value); }
Returning a Value from a Generic Function
We already know that you can create some functions where each takes a different type of parameter. Here are examples from our earlier introduction:
using static System.Console; // Calling a function that takes a string as argument WriteLine(string.Format("Employee Name: " + ShowAString("Gertrude Monay"))); // Calling a function that takes a double-precision number as argument WriteLine($"Hourly Salary: {ShowDouble(28.73)}"); // Calling a function that takes an integer as argument WriteLine("Yearly Salary: " + ShowInteger(55160)); // A parameter of type string string ShowAString(string value) { return value; } // A parameter of a double-precision types string ShowDouble(double value) { return value.ToString(); } // A parameter of an integer type string ShowInteger(int value) { return value.ToString(); }
In the same way, when creating a generic function, you can make it return a value of your choice and call the function when necessary. Here is an example:
using static System.Console;
// Calling a function that takes a string as argument
WriteLine(string.Format("Employee Name: " + Show("Gertrude Monay")));
// Calling a function that takes a double-precision number as argument
WriteLine($"Hourly Salary: {Show(28.73)}");
// Calling a function that takes an integer as argument
WriteLine("Yearly Salary: " + Show(55160));
string Show<T>(T value)
{
return value.ToString();
}
Remember that, when calling the function, you can include the type of the generic value. Here are examples:
using static System.Console; // Calling a function that takes a string as argument WriteLine(string.Format("Employee Name: " + Show<string>("Gertrude monay"))); // Calling a function that takes a double-precision number as argument WriteLine($"Hourly Salary: {Show<double>(28.73)}"); // Calling a function that takes an integer as argument WriteLine("Yearly Salary: " + Show<int>(55160)); string Show<T>(T value) { return value.ToString(); }
Generics and the Null-Forgiving Operator
Remember that an important characteristic of a generic function is that, when you are creating the function, you are not indicating the type of value that the function will actually process. As a result, at the time you are creating the function, if you are using Microsoft Visual Studio, its Intellisense may display a (friendly) warning to let you know that the value that the function will return could be null when the function is eventually called:
You can ignore the warning if you want but it is a good idea to let the compiler know that you are aware of the issue. As seen when studying null operators, to let the compiler that you are familiar with the issue and you will address it when calling the function, you can apply the null-forgiving operator (!) to the value. This can be done as follows:
using static System.Console; // Calling a function that takes a string as argument WriteLine(string.Format("Employee Name: " + Show<string>("Gertrude Monay"))); // Calling a function that takes a double-precision number as argument WriteLine($"Hourly Salary: {Show<double>(28.73)}"); // Calling a function that takes an integer as argument WriteLine("Yearly Salary: " + Show<int>(55160)); string Show<T>(T value) { return value!.ToString()!; }
A Generic Function With Various Parameters
Introduction
Just like a function can use more than one parameter, a function can use various generic parameters. You can specify one parameter as a known type and the other as a generic type. Here is an example:
string Show<T>(string msg, T value)
{
return $"{msg}, {value}";
}
When calling the function, make sure you pass the desired but appropriate value for each argument. Here are examples:
using static System.Console; Show("Employee", "Record"); Show("===============================", ""); Show("Employee Name: ", "Gertrude Monay"); Show("-------------------------------", ""); Show("Hourly Salary: ", 28.73); Show("-------------------------------", ""); Show("Is Full Time? ", true); Show("-------------------------------", ""); Show("Yearly Salary: ", 55160); Show("===============================", ""); void Show<T>(string msg, T value) { WriteLine($"{msg} {value}"); }
This would produce:
Employee Record =============================== Employee Name: Gertrude Monay ------------------------------- Hourly Salary: 28.73 ------------------------------- Is Full Time? True ------------------------------- Yearly Salary: 55160 =============================== Press any key to close this window . . .
Although we directly passed the values to the function when calling it, you can first declare a variable before passing it to the function.
Remember that, when calling a generic function, you can apply angle-brackets to the function and specify the type of the argument. Here are examples:
using static System.Console; Show<string>("Employee", "Record"); Show("===============================", ""); Show<string>("Employee Name: ", "Gertrude Monay"); Show("-------------------------------", ""); Show<double>("Hourly Salary: ", 28.73); Show("-------------------------------", ""); Show<bool>("Is Full Time? ", true); Show("-------------------------------", ""); Show<int>("Yearly Salary: ", 55160); Show("===============================", ""); void Show<T>(string msg, T value) { WriteLine($"{msg} {value}"); }
A Generic Function With Various Parameter Types
As seen above, you can create a function that uses various parameters. In the same way, you can create a generic function that uses various parameter types. To pass two parameter types to a function, inside its <> operator, enter the names of two parameter types separated by a comma. Here is an example:
void Show<OneType, AnotherType>()
{
}
If you want to use the parameter types, you can pass an argument for each to the function. Here are examples:
void Show<OneType, AnotherType>(OneType first,
AnotherType second)
{
}
Remember that each parameter type represents a data type; so you can use it as the type of an argument.
In the body of the function, you can ignore or use the arguments. For example, you can involve them in one or more expressions. Here is an example:
string Show<OneType, AnotherType>(OneType first, AnotherType second) { return $"First: {first}, Second: {second}"; }
By tradition, habit, or simplicity, when a generic function uses two parameter types, the first type is named U and the second type is named V. Here is an example:
string Show<U, V>(U first, V second) { return $"First: {first}, Second: {second}"; }
When a generic function uses three parameter types, the first type can be named U, the second type can be named V, and the third type van be named W.
Calling a Generic Function With Various Parameter Types
To call a function that takes various parameters, you can simply pass the value of each argument as we have done so far. You can also pass the names of variables. Here are examples:
using static System.Console; void Show<U, V>(U first, V second) { WriteLine($"First: {first}; Second: {second}"); } int iValue = 9; var message = "Integer"; Show(message, iValue); message = "Character"; string strValue = "@"; Show(message, strValue); message = "Decimal"; double dValue = 827_838_575.694683; Show(message, dValue);
This would produce:
First: Integer; Second: 9 First: Character; Second: @ First: Decimal; Second: 827838575.694683 Press any key to close this window . . .
An alternative is to specify the type of each argument. To do this, inside the <> operator on the right side of the name of the function, enter the data types separated by a comma. The arguments can be of the same type or of different types. It is up to you to determine the type of a particular argument when calling the function. Here are examples:
using static System.Console; void Show<U, V>(U first, V second) { WriteLine($"First: {first}; Second: {second}"); } Title = "Generic Examples"; int iValue = 9; var message = "Integer"; Show<string, int>(message, iValue); message = "Character"; string strValue = "@"; Show<string, string>(message, strValue); message = "Decimal"; double dValue = 827_838_575.694683; Show<string, double>(message, dValue);
Generic Functions and Optional Arguments
As seen previously, one of the arguments of a generic function can be a parameter type while the other is a known type. Consider this example:
string Show<T>(string msg, T value)
{
return $"{msg}, {value}";
}
void Create()
{
var number = Show<int>("Integer", 246);
string character = Show<char>("Character", ";G");
var floating = Show<double>("Decimal", 355.65);
}
If you want to assign default values to arguments, only the known data type(s) can receive optional arguments. There is no default value for the parameter type because its type is not known. Consider the following function:
string Show<T>(string msg = "Number", T value = null)
{
return $"{msg}, {value}";
}
void Create()
{
var number = Show<int>("Integer", 246);
string character = Show<char>("Character", "G");
var floating = Show<double>("Decimal", 355.65);
}
This would produce the following error:
Error 1 A value of type '<null>' cannot be used as a default parameter because there are no standard conversions to type 'T' C:\Users\Administrator\AppData\Local\Temporary Projects\exo1\Exercise.cs 5 58 GenericInheritance
As a result, if you are creating a function that takes more than one argument and you want to use default values, the parameter type(s) must come first and the one (those) with optional value(s) must come last. The parameter type(s) must not have default value(s). Consider the following code:
string Show<T>(T value, string msg = "Number")
{
return $"{msg}, {value}";
}
void Create()
{
var number = Show<int>(246);
string character = Show<char>("G", "Character");
var floating = Show<double>(355.65);
}
Generic Functions and Named Arguments
When calling a function that takes one or more arguments, you can use the name of an argument followed by a colon and followed by the desired value. This is also valid for parameter types. Here are examples:
using static System.Console; WriteLine("Generic Values"); WriteLine("------------------------------------"); var a = 20.64; var b = 797.24; string strResult = Evaluate<double>(a, b, ""+""); WriteLine("Operation: {0}", strResult); var r = "Research"; var d = "Development"; var oper = "&"; strResult = Evaluate<string>(operation: oper, right: d, left: r); WriteLine(string.Format("Expression: {0}", strResult)); strResult = Evaluate<int>(right: 14159, left: 31, operation: "."); WriteLine($"Decimal Number: {strResult}"); WriteLine("===================================="); string Evaluate<T>(T right, T left, string operation) { return $"{left} {operation} {right}"; }
This would produce:
Generic Values ------------------------------------ Operation: 797.24 20.64 Expression: Research & Development Decimal Number: 31 . 14159 ==================================== Press any key to close this window . . .
Generic Methods
Introduction
Imagine you want to pass different types of values to various methods to primarily accomplish the same purpose. One way to solve this problem is to overload a method in various versions. Here is an example:
public class Exercise { // Use an integer as parameter void Show(int value) { } // Use a double-precision value as parameter void Show(double value) { } // Use a character as parameter void Show(char value) { } }
You can then call the method using the version(s) you want. Here are examples:
using static System.Console; Exercise exo = new Exercise(); exo.Show(246); exo.Show('G'); exo.Show(355.65); return 1_001; public class Exercise { // A method that uses an integer as parameter public void Show(int value) { WriteLine("Natural Number: " + value.ToString()); } // A method that uses a double-precision value as parameter public void Show(double value) { WriteLine($"Floating - Point Number: {value}"); } // A method that uses a character as parameter public void Show(char value) { WriteLine("Character: {0}", value); } }
This would produce:
Natural Number: 246 Character: G Floating - Point Number: 355.65 Press any key to close this window . . .
We passed a value directly to the method when we called it. As an alternative, you can first declare a variable, assign it a value, and then pass that variable to the method. Here are examples:
using static System.Console;
Exercise exo = new Exercise();
int value1 = 246;
char value2 = 'G';
double value3 = 355.65;
exo.Show(value1);
exo.Show(value2);
exo.Show(value3);
return 1_001;
public class Exercise
{
public void Show(int value)
{
WriteLine("Natural Number: " + value.ToString());
}
public void Show(double value)
{
WriteLine($"Floating - Point Number: {value}");
}
public void Show(char value)
{
WriteLine("Character: {0}", value);
}
});
Although this is based on the concept of method overloading, another way you can solve this type of problem is to create a generic method. This means that you can create a method that follows what we learned about generic functions; that is, a method that doesn't "know" the type of value that would be passed to it.
Creating a Generic Method
A generic method is created like a generic function, except that the method is created as a member funciton of a class. Therefore, to create a generic method, on the right side of the name of the method, type the <> operator. Inside the <> operator, type a letter or a name as the parameter type. Here is an example:
public class Exercise
{
public void Show<TypeOfValue>()
{
}
}
Remember that, by tradition, a parameter type is represented with a letter, in uppercase. Here is an example:
public class Exercise
{
public void Show<T>()
{
}
}
As mentioned with generic functions, in the body of the method, you can ignore or use the argument.
After applying a parameter type to a generic method, you can use that parameter type. As seen with generic functions, in the parentheses of the method, you can create a parameter of the parameter type. Probably the minimum way to use the parameter is to return to it from the method. Here is an example:
void Show<T>(T value)
{
return value.ToString();
}
Calling a Generic Method
You should already know that the primary difference between a function and a method is that you must use a class to access a method. This approach also applies for generic methods. Here is an example:
using static System.Console; Exercise exo = new(); int value1 = 246; char value2 = 'G'; double value3 = 355.65; exo.Show(value1); exo.Show(value2); exo.Show(value3); return 1_001; public class Exercise { public void Show<T>(T value) { WriteLine(string.Format("Value: {0}", value.ToString())); } }
Remember that, as mentioned with generic functions, when calling the method, you can apply the angle-brackets and include a data type. Here are examples:
using static System.Console; Exercise exo = new Exercise(); exo.Show<int>(246); exo.Show<char>('G'); exo.Show<double>(355.65); return 1_001; public class Exercise { public void Show<T>(T value) { WriteLine(string.Format("Value: {0}", value.ToString())); } }
Applying the Null-Forgiving Operator
Also remember that, if you are using Microsoft Visual Studio to develop your application, its intellisense may warn you that the generic argument you are using may have have a null value and that you should be prepared to deal with that issue. In this case, to let the compiler know that you know what you are doing, you can apply the null-forgiving operator. Here is an example:
using static System.Console;
Exercise exo = new Exercise();
exo.Show<int>(246);
exo.Show<char>('G');
exo.Show<double>(355.65);
return 1_001;
public class Exercise
{
public void Show<T>(T value)
{
WriteLine(string.Format("Value: {0}", value!.ToString()));
}
}
Returning a Value from a Generic Method
As seen with generic functions, you can create a generic method that returns a value. Here is an example:
using static System.Console;
Exercise exo = new Exercise();
WriteLine("Value: {0}", exo.Show(246));
WriteLine("Value: {0}", exo.Show('G'));
WriteLine("Value: {0}", exo.Show(355.65));
return 1_001;
public class Exercise
{
public string Show<T>(T value)
{
return value!.ToString()!;
}
}
A Generic Method With Various Parameters
As seen with generic functions, you can create a generic method that use more than one parameter type. In that case, in the angle-brackets, separate the parameter types with a comma. Everything else is as we saw for generic functions.
Generic Functions and Optional Arguments
As seen with generic functions, the parameters of a generic method can use optional values. The rules are the same ww saw for generic functions.
Generic Methods and Named Arguments
As seen with generic functions, when calling a generic method that takes one or more arguments, you can use the name of an argument followed by a colon and followed by the desired value.
Overloading a Generic Method
In C#, you cannot currently overload a function, but you can overload a method (a member function in a class). There are various ways you can overload a method, and generic methods bring new features. One version of a method can be a generic type while the other is not. In this case, even if the metho don't use any parameter, there would not be a name conflict. Consider the following example:
public class ElectriBill
{
void Create()
{
}
void Create<T>()
{
}
}
You can have two methods that take the same types and numbers of arguments. In order to avoid name conflict, one of the methods can (must) be generic. Here is an example:
public class ElectriBill
{
void Create(string msg)
{
}
void Create<T>(string msg)
{
}
string Show<T>(string msg, int value)
{
}
string Show<OneType, AnotherType>(string msg, int value)
{
}
}
Finally, you can have methods that use different types and/or numbers of arguments. Here is an example:
public class ElectriBill { string Show<T>(string msg, T value) { return $"{msg}, {value}"; } string Show<OneType, AnotherType>(OneType first, AnotherType second) { return $"First: {first}, Second: {second}"; } }
|
|||
Previous | Copyright © 2008-2025, FunctionX | Monday 16 June 2025, 08:50 | Next |
|