Introduction to Generics
Introduction to Generics
Generic Functions
Introduction
Imagine you want to pass different types of values to various functions to primarily accomplish the same purpose. You may be tempted to overload a function in various versions as follows:
using System.Windows.Forms; namespace GenericsFundamentals { public partial class Form1 : Form { public Form1() { InitializeComponent(); } // 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 functions. Here are examples:
using System; using System.Windows.Forms; namespace GenericsFundamentals { public partial class Form1 : Form { // Use an integer as parameter void Show(int value) { MessageBox.Show("Natural Number: " + value.ToString(), "Generic Examples"); } // Use a double-precision value as parameter void Show(double value) { MessageBox.Show($"Floating - Point Number: {value}", "Generic Examples"); } // Use a character as parameter void Show(char value) { MessageBox.Show(string.Format("Character: {0}", value), "Generic Examples"); } public Form1() { InitializeComponent(); // Call the function that takes an integer as argument Show(246); // Call the version of the function that takes a character as argument Show('G'); // Call the version of the function that takes a decimal as argument Show(355.65); } } }
This would produce:
We passed a constant value directly to the method when we called it. Remember that you can also first declare a variable, assign it a value, and then pass that variable to the method. Here are examples:
using System.Windows.Forms; namespace GenericsFundamentals { public partial class Form1 : Form { public Form1() { InitializeComponent(); int value1 = 246; char value2 = 'G'; double value3 = 355.65; // Call the function that takes an integer as argument Show(value1); // Call the version of the function that takes a character as argument Show(value2); // Call the version of the function that takes a decimal as argument Show(value3); } // Use an integer as parameter void Show(int value) => MessageBox.Show("Natural Number: " + value.ToString(), "Generic Examples"); // Use a double-precision value as parameter void Show(double value) => MessageBox.Show($"Floating - Point Number: {value}", "Generic Examples"); // Use a character as parameter void Show(char value) => MessageBox.Show(string.Format("Character: {0}", value), "Generic Examples"); } }
Although this is based on the concept of function overloading, another way you can solve this type of problem is to create one function that "doesn't know" the type of value that would be passed to it but the function is equipped to process the value somehow. Based on the above code, you can create one function that takes an argument and uses its value. To do this, at the time you are defining the function, you only let the function know that it would receive an argument but you don't specify the type of value that it will process. Such a function is referred to as generic.
Creating a Generic Function
A generic function is a function that can process a value whose type is known only when the variable is accessed. To create a generic function, on the right side of the name of the function, type the <> operator. 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, and based on our earlier code, you can simply convert it 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
As mentioned earlier, one of the particularities of a generic function is that, at the time it is defined, the function doesn't know the type of the 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 directly passing (a constant of) the type of value that the function will process. Here are different examples of calling our Show() function:
using System.Windows.Forms; namespace GenericsFundamentals { public partial class Form1 : Form { void Show<T>(T value) { MessageBox.Show(string.Format("Value: {0}", value.ToString()), "Generic Examples"); } public Form1() { InitializeComponent(); int value1 = 246; char value2 = 'G'; double value3 = 355.65; // Call the version that takes an integer as argument Show(value1); // Call the version that takes a character as argument Show(value2); // Call the version that takes a decimal number as argument Show(value3); } } }
This would produce:
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 constant value that will be processed. Here are examples:
using System.Windows.Forms; namespace GenericsFundamentals { public partial class Form1 : Form { void Show<T>(T value) { MessageBox.Show(string.Format("Value: {0}", value.ToString()), "Generic Examples"); } public Form1() { InitializeComponent(); int value1 = 246; char value2 = 'G'; double value3 = 355.65; // Call the version that takes an integer as argument Show<int>(value1); // Call the version that takes a character as argument Show<char>(value2); // Call the version that takes a decimal number as argument Show<double>(value3); } } }
Returning a Value from a Generic Function
We already know that you can create an overloaded function where each version takes a different type of parameter. Here are examples from our earlier introduction:
using System.Windows.Forms; namespace GenericsFundamentals { public partial class Form1 : Form { public Form1() { InitializeComponent(); // Call the function that takes an integer as argument MessageBox.Show("Natural Number: " + Show(246), "Generic Examples"); // Call the version of the function that takes a character as argument MessageBox.Show(string.Format("Character: {0}", Show('G')), "Generic Examples"); // Call the version of the function that takes a decimal as argument MessageBox.Show($"Floating - Point Number: {Show(355.65)}", "Generic Examples"); } // Use an integer as parameter string Show(int value) { return value.ToString(); } // Use a double-precision value as parameter string Show(double value) { return value.ToString(); } // Use a character as parameter string Show(char 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 System.Windows.Forms; namespace GenericsFundamentals { public partial class Form1 : Form { string Show<T>(T value) { return value.ToString(); } public Form1() { InitializeComponent(); // Call the function that takes an integer as argument MessageBox.Show("Natural Number: " + Show(246), "Generic Examples"); // Call the version of the function that takes a character as argument MessageBox.Show(string.Format("Character: {0}", Show('G')), "Generic Examples"); // Call the version of the function that takes a decimal as argument MessageBox.Show($"Floating - Point Number: {Show(355.65)}", "Generic Examples"); } } }
Remember that, when calling the function, you can include the type of the generic value. Here are examples:
using System.Windows.Forms; namespace GenericsFundamentals { public partial class Form1 : Form { string Show<T>(T value) { return value.ToString(); } public Form1() { InitializeComponent(); // Call the function that takes an integer as argument MessageBox.Show("Natural Number: " + Show<int>(246), "Generic Examples"); // Call the version of the function that takes a character as argument MessageBox.Show(string.Format("Character: {0}", Show<char>('G')), "Generic Examples"); // Call the version of the function that takes a decimal as argument MessageBox.Show($"Floating - Point Number: {Show<double>(355.65)}", "Generic Examples"); } } }
A Generic Function With Various Parameters
Introduction
Just like a function can take one argument, it can use various generic parameters. You can pass 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 argument for each call. Here are examples:
using System.Windows.Forms; namespace GenericsFundamentals { public partial class Form1 : Form { void Show<T>(string msg, T value) { MessageBox.Show($"{msg}: {value}", "Generic Examples"); } public Form1() { InitializeComponent(); int value1 = 246; char value2 = 'G'; double value3 = 355.65; // Call the version that takes an integer as argument Show<int>("Integer", value1); // Call the version that takes a character as argument Show<char>("Character", value2); // Call the version that takes a decimal number as argument Show<double>("Decimal", value3); } } }
Although we directly passed the values to the function when calling it, you can first declare a variable before passing it to the function. Here are examples:
using System.Windows.Forms; namespace GenericsFundamentals { public partial class Form1 : Form { void Show<T>(string msg, T value) { MessageBox.Show($"{msg}: {value}", "Generic Examples"); } public Form1() { InitializeComponent(); const int iValue = 246; var message = "Integer"; Show(message, iValue); message = "Character"; const char cValue = 'G'; Show(message, cValue); message = "Decimal"; const double dValue = 355.65; Show(message, dValue); } } }
A Generic Function With Various Parameter Types
As seen above, you can pass different arguments to a function. You can also pass different types of parameters, in any appropriate order of your choice, to a method. 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:
public class Generator
{
public void Show<FirstType, SecondType>()
{
}
}
If you want to use the parameter types, you can pass an argument for each to the function. Remember that each parameter type represents a data type; so you can use it as the type of an argument. Here are examples:
public class Generator
{
public void Show<FirstType, SecondType>(FirstType first,
SecondType second)
{
}
}
In the body of the function, you can then use the arguments as you see fit. For example, you can involve them in one or more expressions. Here is an example:
public class Generator { public string Show<FirstType, SecondType>(FirstType first, SecondType 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:
public class Generator { public 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 System.Windows.Forms; namespace GenericsFundamentals { public partial class Form1 : Form { void Show<U, V>(U first, V second) { MessageBox.Show($"First: {first}; Second: {second}", "Generic Examples"); } public Form1() { InitializeComponent(); const int iValue = 246; var message = "Integer"; Show(message, iValue); message = "Character"; const char cValue = 'G'; Show(message, cValue); message = "Decimal"; const double dValue = 355.65; Show(message, dValue); } } }
This would produce:
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 different types. It is up to you to determine the type of a particular argument when calling the function. Here are examples:
using System.Windows.Forms; namespace GenericsFundamentals { public partial class Form1 : Form { void Show<U, V>(U first, V second) { MessageBox.Show($"First: {first}; Second: {second}", "Generic Examples"); } public Form1() { InitializeComponent(); const int iValue = 246; var message = "Integer"; Show<string, int>(message, iValue); message = "Character"; const char cValue = 'G'; Show<string, char>(message, cValue); message = "Decimal"; const double dValue = 355.65; Show<string, double>(message, dValue); } } }
Generic Functions and Optional Arguments
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);
dynamic 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);
dynamic 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);
dynamic 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, when calling it, 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; namespace IntroductoryGenerics { public class Program { static string Evaluate<T>(T right, T left, char operation) { return $"{left} {operation} {right}"; } static void Prepare() { var a = 20.64; var b = 797.24; string strFirstResult = Evaluate<double>(a, b, '+'); var r = "Research "; var d = " Development"; var oper = '&'; string strSecondResult = Evaluate<string>(operation: oper, right: d, left: r); string strThirdResult = Evaluate<int>(right: 14159, left: 31, operation: '.'); } public static int Main(string[] args) { Title = "Generic Vaplues"; 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("======================================="); return 1; } } }
This would produce:
Generic Values ------------------------------------ Operation: 797.24 + 20.64 Expression: Research & Development Decimal Number: 31 . 14159 ======================================= Press any key to continue . . .
Overloading a Generic Function
As we have seen in previous lessons, there are various ways you can overload a function, and generic functions bring new features. One version of a function can be a generic type while the other is not. In this case, even if the functions don't use any parameter, there would not be a name conflict. Here is an example:
public cusing System.Windows.Forms;
namespace GenericsFundamentals
{
public partial class Form1 : Form
{
void Create()
{
}
void Create<T>()
{
}
}
}
You can have two functions that take the same types and numbers of arguments. In order to avoid name conflict, one of the functions can (must) be generic. Here is an example:
using System.Windows.Forms;
namespace GenericsFundamentals
{
public partial class Form1 : Form
{
void Create(string msg)
{
}
void Create<T>(string msg)
{
}
string Show<T>(string msg, int value)
{
}
string Show<FirstType, SecondType>(string msg, int value)
{
}
}
}
Finally, you can have functions that use different types and/or numbers of arguments. Here is an example:
using System.Windows.Forms; namespace GenericsFundamentals { public partial class Form1 : Form { string Show<T>(string msg, T value) { return $"{msg}, {value}"; } string Show<FirstType, SecondType>(FirstType first, SecondType second) { return $"First: {first}, Second: {second}"; } } }
Generic Classes
Generic Types and Methods
As a reminder, in our lessons, a function is an action that is created in the document that contains the Main(string[] args) function for a console application, or in a document that contains the code of a form.
Everything we learned so far about generic functions is directly applicable to the methods of a class, as generic methods. For example, we had learned to create generic functions. In the same way, we can create a generic function as a member of a class, making it a generic method. Here is an example:
namespace IntroductoryGenerics
{
public class Generator
{
public void Show<T>(T value)
{
}
}
}
The main difference between what we call a function and a method is that, to use a method outside the class, you must declare a variable of the class and then call the method. Here are examples:
using static System.Console; namespace IntroductoryGenerics { public class Program { public static int Main(string[] args) { Title = "Generic Vaplues"; WriteLine("Generic Values"); WriteLine("----------------"); int value1 = 246; char value2 = 'G'; double value3 = 355.65; Generator gen = new Generator(); // Call the version that takes an integer as argument gen.Show(value1); // Call the version that takes a character as argument gen.Show(value2); // Call the version that takes a decimal number as argument gen.Show(value3); WriteLine("================================="); return 111; } } public class Generator { public void Show<T>(T value) { WriteLine(string.Format("Value: {0}", value.ToString()), "Generic Examples"); } } }
Introduction to Creating a Generic Class
Like a function, a class can be created as a generic one. When a class is created as generic, it is asked to process a value without knowing what type that value is. This means that the class will know the type of value only when its object is accessed.
To create a generic class, on the right side of the name of the class, type the <> operator and enter a name for the parameter type. Here is an example:
public class Generator<TypeOfValue>
{
}
This parameter type is just a representative of a data type. As a data type, you can use it to declare a variable in the body of the class. Here is an example:
public class Generator<TypeOfValue>
{
public TypeOfValue value;
}
After declaring such a variable, you can use it in your project. For example, you can access it outside the class using the period operator. Inside the class, one way you can use the variable is to involve it in an expression in one of the methods of the class. Here is an example:
public class Generator<TypeOfValue>
{
public TypeOfValue Value;
public string Show()
{
return Value.ToString();
}
}
Using a Generic Class
After creating a generic class, you can use it. One way to do this, as we have learned in previous lessons, consists of declaring a variable for it. In previous lessons, to declare a variable of a class, we would write:
SomeClass sc = new SomeClass();
If the class is generic, on the right side of its name when declaring the variable, type the <> operator. Inside this operator, enter the data type that will be processed as the parameter type of the generic class. Here is an example:
namespace IntroductoryGenerics { public class Program { public static void Main(string[] args) { Generator<int> gen = new Generator<int>(); } } }
You can also declare a variable of a generic type using the var or the dynamic keyword. To do this, use var or dynamic on the left side of the variable name, omit the <> operator and the data type. Here are examples:
namespace IntroductoryGenerics
{
public class Program
{
public static void Main(string[] args)
{
Generator<int> gen = new Generator<int>();
var calculation = new Generator<int>();
dynamic expression = new Generator<int>();
}
}
}
After declaring the variable (whether using the name of the class or the var or the dynamic keyword), you can then access the member(s) of the class using the period operator. Here are examples:
using static System.Console; namespace IntroductoryGenerics { public class Program { public static int Main(string[] args) { Title = "Generic Vaplues"; WriteLine("Generic Values"); WriteLine("------------------------------------"); Generator<int> gen = new Generator<int>(); var calculation = new Generator<char>(); dynamic expression = new Generator<double>(); WriteLine("A Value: {0}", gen.Value); WriteLine(string.Format("A Calculation: {0}", calculation.Value)); WriteLine($"An Expression: {expression.Value}"); WriteLine("======================================="); return 11; } } public class Generator<TypeOfValue> { public TypeOfValue Value; public string Show() { return Value.ToString(); } } }
This would produce:
Generic Values ------------------------------------ A Value: 0 A Calculation: An Expression: 0 ======================================= Press any key to continue . . .
Passing a Parameter Type to a Method
We saw that you could declare a variable of a parameter type in a generic class. Another way you can use it is to pass it as an argument to a method and make the argument a parameter type. As seen previously, you can use the argument as you see fit. For example, you can display its value to the user. Here is an example:
using static System.Console; namespace IntroductoryGenerics { public class Program { public static int Main(string[] args) { Title = "Generic Vaplues"; WriteLine("Generic Values"); WriteLine("----------------"); var iValue = 246; Generator<int> gen = new Generator<int>(); gen.Show(iValue); char cValue = 'G'; var calculation = new Generator<char>(); calculation.Show(cValue); double nbr = 355.65; dynamic expression = new Generator<double>(); expression.Show(nbr); WriteLine("================================="); return 111; } } public class Generator<TypeOfValue> { public void Show(TypeOfValue value) { WriteLine("Value: {0}", value); } } }
This would produce:
Generic Values ---------------- Value: 246 Value: G Value: 355.65 ================================= Press any key to continue . . .
In the same way, you can pass the parameter type to a constructor of the class. Here is an example:
public class Generator<T>
{
public T Value;
public Generator(T value)
{
}
}
Since the generic parameter and the parameter of the constructor are the same type, you can involve them in the same expression, such as assigning one to the other. Here is an example:
public class Generator<T>
{
public T Value;
public Generator(T value)
{
Value = value;
}
}
Returning a Parameter Type
Besides, or as opposed to, passing a parameter type, in the class, you can create a method that returns a parameter type. Once again, you can primarily observe the rules we reviewed for returning a value from a function. Here is an example:
public class Generator<T>
{
public T Value;
public Generator(T value)
{
this.Value = value;
}
private T Create()
{
return this.Value;
}
}
A Property of a Parameter Type
You can create a property that is of the parameter type of the generic class. There is no significant rule to follow when creating the property, except that you should remember that, at the time you are creating the property, the class "doesn't know" the type of the parameter. Here is an example:
public class Generator<T>
{
public T val;
public Generator(T value)
{
val = value;
}
public T Value
{
get { return val; }
set { val = value; }
}
}
Options on Generic Classes
A Generic Class with Multiple Type Parameters
As done for generic functions, when creating a generic class, you can specify more than one parameter type. To do this, in the <> operator, after the first generic type, enter a comma and another generic type. Here is an example:
public class Generator<FirstType, SecondType>
{
}
Once again, remember that, to make the parameter types simple to read, you can name them U, V, etc.
If you know for sure that the parameters will be of the same type, you can use one method to process both. Otherwise, you can declare the necessary members for each type. You can also create a method that would take many arguments with each argument of a particular type. Here are examples:
public class Generator<U, V>
{
private U t;
private V v;
public void SetTValue(U value)
{
t = value;
}
public U GetTValue()
{
return t;
}
public void SetVValue(V value)
{
v = value;
}
public V GetVValue()
{
return v;
}
public string Show(U tValue, V vValue)
{
return $"{tValue}{vValue}";
}
}
When declaring a variable for the class, make sure you appropriately specify the list of parameter types. Here are two examples:
using static System.Console; namespace IntroductoryGenerics { public class Program { public static int Main(string[] args) { Title = "Generic Vaplues"; WriteLine("Generic Values"); WriteLine("----------------"); Generator<int, int> yearlySalary = new Generator<int, int>(); yearlySalary.SetTValue(84); yearlySalary.SetVValue(680); string strNumber = yearlySalary.Show(yearlySalary.GetTValue(), yearlySalary.GetVValue()); WriteLine("Yearly Salary: {0}", strNumber); Generator<char, double> price = new Generator<char, double>(); price.SetTValue('$'); price.SetVValue(185.45); string strValues = price.Show(price.GetTValue(), price.GetVValue()); WriteLine(string.Format("Item Price: {0}", strValues)); Generator<short, decimal> disparate = new Generator<short, decimal>(); disparate.SetTValue(425); disparate.SetVValue(.35m); strNumber = disparate.Show(disparate.GetTValue(), disparate.GetVValue()); WriteLine($"Some Number: {strNumber}"); Generator<int, string> company = new Generator<int, string>(); company.SetTValue(1800); company.SetVValue("flowerslovers.com"); strNumber = company.Show(company.GetTValue(), company.GetVValue()); WriteLine($"Web Site: {strNumber}"); WriteLine("================================="); return 111; } } public class Generator<U, V> { private U t; private V v; public void SetTValue(U value) { t = value; } public U GetTValue() { return t; } public void SetVValue(V value) { v = value; } public V GetVValue() { return v; } public string Show(U tValue, V vValue) { return $"{tValue}{vValue}"; } } }
This would produce:
Generic Values ---------------- Yearly Salary: 84680 Item Price: $185.45 Some Number: 4250.35 Web Site: 1800flowers.com ================================= Press any key to continue . . .
You can also declare the variable using the var or the dynamic keyword. To do this, on the left side of the assignment operator, type only the var or the dynamic keyword and the name of the variable. Here is an example:
using static System.Console; namespace IntroductoryGenerics { public class Program { public static int Main(string[] args) { Title = "Generic Vaplues"; WriteLine("Generic Values"); WriteLine("--------------------------------"); Generator<int, int> yearlySalary = new Generator<int, int>(); yearlySalary.SetTValue(84); yearlySalary.SetVValue(680); string strNumber = yearlySalary.Show(yearlySalary.GetTValue(), yearlySalary.GetVValue()); WriteLine("Yearly Salary: {0}", strNumber); Generator<char, double> price = new Generator<char, double>(); price.SetTValue('$'); price.SetVValue(185.45); string strValues = price.Show(price.GetTValue(), price.GetVValue()); WriteLine(string.Format("Item Price: {0}", strValues)); var disparate = new Generator<short, decimal>(); disparate.SetTValue(425); disparate.SetVValue(.35m); strNumber = disparate.Show(disparate.GetTValue(), disparate.GetVValue()); WriteLine($"Some Number: {strNumber}"); dynamic company = new Generator<int, string>(); company.SetTValue(1800); company.SetVValue("flowers.com"); strNumber = company.Show(company.GetTValue(), company.GetVValue()); WriteLine($"Web Site: {strNumber}"); WriteLine("================================="); return 111; } } public class Generator<U, V> { private U t; private V v; public void SetTValue(U value) => t = value; public U GetTValue() => t; public void SetVValue(V value) => v = value; public V GetVValue() => v; public string Show(U tValue, V vValue) => $"{tValue}{vValue}"; } }
If a generic class has more than one parameter type, they don't have to be of the same type. At the time you are creating the class, you may not specify their types but you can anticipate that they would be different. It is when you declare the variable that you would need to determine their precise types.
Even if the parameters are of primitive types, you can first declare the variables and pass them to the class.
A Class as a Parameter Type
So far, in our examples, we treated the parameter type as a primitive data type. A parameter type can also be a formal class, either one that you created yourself or one that exists in any available library, including the .NET Framework. When creating the generic class, you must follow all the rules we have reviewed so far for generic classess. Here is such a simple class:
public class Generator { public string Show(TypeOfValue val) { return $"{val}"; } }
As mentioned already, the class that would be processed by the generic class must have been previously created so it can be used as a parameter. Here is an example:
public class FourSideGeometricFigure { public string Name { get; set; } public double Base { get; set; } public double Height { get; set; } public double Area { get { return Base * Height; } } public override string ToString() { return $"Type: {Name}, Base: {Base}, Height: {Height}, Area: {Area}"; } } public class Generator<T> { public string Show(T val) { return val.ToString(); } }
When declaring a variable of the generic class, make sure you enter the name of the normal class in place of the parameter type. You can declare the variable using its name or using the var or the dynamic keyword. Here are examples:
using static System.Console; namespace IntroductoryGenerics { public class Program { public static int Main(string[] args) { Title = "Generic Vaplues"; WriteLine("Generic Values"); WriteLine("--------------------------------"); FourSideGeometricFigure sqr = new FourSideGeometricFigure(); sqr.Name = "Square"; sqr.Base = 36.82; sqr.Height = 36.82; Generator<FourSideGeometricFigure> exoSquare = new Generator<FourSideGeometricFigure>(); WriteLine("Shape Summary\n\t{0}", exoSquare.Show(sqr)); WriteLine("--------------------------------"); var rect = new FourSideGeometricFigure(); rect.Name = "Rectangle"; rect.Base = 52.94; rect.Height = 27.58; dynamic exoRect = new Generator<FourSideGeometricFigure>(); WriteLine(string.Format("hape Summary\n\t{0}", exoRect.Show(rect))); WriteLine("================================="); return 1111; } } public class FourSideGeometricFigure { public string Name { get; set; } public double Base { get; set; } public double Height { get; set; } public double Area { get { return Base * Height; } } public override string ToString() { return $"Type: {Name}\n\tBase: {Base}\n\tHeight: {Height}\n\tArea: {Area}"; } } public class Generator<T> { public string Show(T val) { return val.ToString(); } } }
This would produce:
Generic Values -------------------------------- Shape Summary Type: Square Base: 36.82 Height: 36.82 Area: 1355.7124 -------------------------------- hape Summary Type: Rectangle Base: 52.94 Height: 27.58 Area: 1460.0852 ================================= Press any key to continue . . .
|
||
Previous | Copyright © 2008-2021, FunctionX | Next |
|