Introduction to Generics
Introduction to Generics
Generic Methods
Introduction
Imagine you want to pass different types of values to various methods to primarily accomplish the same purpose. You may be tempted to overloaded a method in various versions as follows:
public class Generator { // Produce a string that hoolds an integer public string Show(int value) { return value.ToString(); } // Produce a string that hoolds a double-precision value public string Show(double value) { return value.ToString(); } // Produce a string that hoolds a character public string Show(char value) { return value.ToString(); } }
You can then call the methods as follows:
<!DOCTYPE html> <html> <head> <title>Generic Examples</title> </head> <body> <h3>Generic Examples</h3> @{ var exo = new Generator(); } @{ // Call the version of the method that displays an integer } <p>Natural Number: @exo.Show(246)</p> @{ // Call the version of the method that displays a character } <p>Character: @exo.Show('G')</p> @{ // Call the version of the method that displays a decimal } <p>Floating-Point Number: @exo.Show(355.65)</p> </body> </html>
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:
<!DOCTYPE html> <html> <head> <title>Generic Values</title> </head> <body> <h3>Generic Values</h3> @{ var exo = new Generator(); } @{ // Call the version of the method that displays an integer var value1 = 246; } <p>Natural Number: @exo.Show(@value1)</p> @{ // Call the version of the method that displays a character var value2 = 'G'; } <p>Character: @exo.Show(@value2)</p> @{ // Call the version of the method that displays a decimal var value3 = 355.65; } <p>Floating-Point Number: @exo.Show(@value3)</p> </body> </html>
Although this is based on the concept of method overloading, another way you can solve this type of problem is to create one method that "doesn't know" the type of value that would be passed to it but the method is equipped to process the value somehow. Based on the above code, you can create one method that takes an argument and uses its value. To do this, at the time you are defining the method, you only let the method know that it would receive an argument but you don't specify the type of value that it will process. Such a method is referred to as generic.
Creating a Generic Method
A generic method is a method that can process a value whose type is known only when the variable is accessed. To create a generic method, on the right side of the name of the method, type the <> operator. Inside of the <> operator, enter a letter or a name, which is referred to as parameter type. Here is an example:
public class Generator
{
public void Show<TypeOfValue>()
{
}
}
One of the ways you can use the parameter type is to pass an argument to the method. You do this by preceding the name of the argument with the parameter type. Here is an example:
public class Generator
{
public void Show<TypeOfValue>(TypeOfValue value)
{
}
}
In the body of the method, 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:
public class Generator
{
public 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 class Generator
{
public void Show<T>(T value)
{
return value.ToString();
}
}
Calling a Generic Method
As mentioned earlier, one of the particularities of a generic method is that, at the time it is defined, the method doesn't know the type of the parameter. This means that, when calling the method, 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 method will process. Here are different examples of calling our Show() method:
<!DOCTYPE html> <html> <head> <title>Generic Values</title> </head> <body> <h3>Generic Values</h3> @{ var exo = new Generator(); } @{ // Call the version of the method that displays an integer dynamic value1 = 246; } <p>Natural Number: @exo.Show(@value1)</p> @{ // Call the version of the method that displays a character dynamic value2 = 'G'; } <p>Character: @exo.Show(@value2)</p> @{ // Call the version of the method that displays a decimal dynamic value3 = 355.65; } <p>Floating-Point Number: @exo.Show(@value3)</p> </body> </html>
As an alternative, you can type the name of the method, followed by angle brackets. Inside of 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:
public class Generator { public string Show<T>(T value) { return value.ToString(); } private void Create() { var number = Show<int>(246); dynamic character = Show<char>('G'); var floating = Show<double>(355.65); } }
You can also declare the value as a constant before passing it to the method.
A Generic Method With Various Parameters
Introduction
Just like a method can take one argument, it can take various generic parameters. You can pass one parameter as a known type and the other as a generic type. Here is an example:
public class Generator
{
public string Show<T>(string msg, T value)
{
return $"{msg}, {value}";
}
}
When calling the method, make sure you pass the appropriate argument for each method. Here are examples:
public class Generator { public string Show<T>(string msg, T value) { return $"{msg}, {value}"; } private void Create() { var number = Show<int>("Integer", 246); dynamic character = Show<char>("Character", 'G'); var floating = Show<double>("Decimal", 355.65); } }
The above code called the method in the same class. In the same way, you can the method in the HTML code. Here are examples
<!DOCTYPE html> <html> <head> <title>Generic Values</title> </head> <body> <h3>Generic Values</h3> @{ var exo = new Generator(); } @{ // Call the version of the method that displays an integer dynamic value1 = 246; } <p>Natural Number: @exo.Show("Integer", @value1)</p> @{ // Call the version of the method that displays a character dynamic value2 = 'G'; } <p>Character: @exo.Show("Character", @value2)</p> @{ // Call the version of the method that displays a decimal dynamic value3 = 355.65; } <p>Floating-Point Number: @exo.Show("Decimal", @value3)</p> </body> </html>
Although we directly passed the values to the method when calling it, you can first declare a variable before passing it to the method. Here are examples:
public class Generator { public string Show<T>(string msg, T value) { return $"{msg}, {value}"; } private void Create() { const int iValue = 246; var message = "Integer"; var number = Show<int>(message, iValue); message = "Character"; const char cValue = 'G'; dynamic character = Show<char>(message, cValue); message = "Decimal"; const double dValue = 355.65; var floating = Show<double>(message, dValue); } }
You can also pass such variables for code mixed with HTML. Here is an example:
<!DOCTYPE html> <html> <head> <title>Generic Values</title> </head> <body> <h3>Generic Values</h3> @{ Generator exo = new Generator(); } @{ // Call the version of the method that displays an integer const int iValue = 246; var message = "Integer"; } <p>Natural Number: @exo.Show(@message, @iValue)</p> @{ // Call the version of the method that displays a character message = "Character"; const char cValue = 'G'; } <p>Character: @exo.Show(@message, @cValue)</p> @{ // Call the version of the method that displays a decimal message = "Decimal"; const double dValue = 355.65; } <p>Floating-Point Number: @exo.Show(@message, @dValue)</p> </body> </html>
A Generic Method With Various Parameter Types
As seen above, you can pass different arguments to a method. 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 method, 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 method. 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 method, 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 method 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 method 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 Method With Various Parameter Types
To call a method 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:
u<!DOCTYPE html> <html> <head> <title>Generic Values</title> </head> <body> <h3>Generic Values</h3> @{ Generator exo = new Generator(); } @{ const int iValue = 246; var message = "Integer"; } <p>Natural Number: @exo.Show(@message, @iValue)</p> @{ message = "Character"; const char cValue = 'G'; } <p>Character: @exo.Show(@message, @cValue)</p> @{ message = "Decimal"; const double dValue = 355.65; } <p>Floating-Point Number: @exo.Show(@message, @dValue)</p> </body> </html>
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 method, 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 method.Here are examples:
public class Generator { public string Show<U, V>(U first, V second) { return $"First: {first}, Second: {second}"; } public void Create(string value1, float value2) { string strResult = Show<string, float>(value1, value2); } public void Prepare(int val1, short val2) { string strResult = Show<int, short>(val1, val2); } public void Process(double value, bool drunk) { string strResult = Show<double, bool>(value, drunk); } }
Generic Methods and Optional Arguments
One of the arguments of a generic method can be a parameter type while the other is a known type. Here is an example we used:
public class Generator
{
public string Show<T>(string msg, T value)
{
return $"{msg}, {value}";
}
private 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 types can receive optional arguments. There is no default value for the parameter type because its type is not known. Consider the following method:
public class Generator
{
public string Show<T>(string msg = "Number", T value = null)
{
return $"{msg}, {value}";
}
private 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 method 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) value(s) must come last. The parameter type(s) must not have default value(s). Consider the following code:
public class Generator
{
public string Show<T>(T value, string msg = "Number")
{
return $"{msg}, {value}";
}
private void Create()
{
var number = Show<int>(246);
dynamic character = Show<char>('G', "Character");
var floating = Show<double>(355.65);
}
}
Generic Methods and Named Arguments
When calling a method 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:
public class Generator { public string Evaluate<T>(T right, T left, char operation) { return $"{left} {operation} {right}"; } private 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: '.'); } }
You can use the same technique if you are writing your code in the HTML side of a webpage:
<!DOCTYPE html> <html> <head> <title>Generic Values</title> </head> <body> <h3>Generic Values</h3> @{ var a = 20.64; var b = 797.24; Generator gen = new Generator(); string strResult = gen.Evaluate<double>(a, b, '+'); } <p>Operation: @strResult</p> @{ var r = "Research "; var d = " Development"; var oper = '&'; strResult = gen.Evaluate<string>(operation: oper, right: d, left: r); } <p>Expression: @strResult</p> @{ strResult = gen.Evaluate<int>(right: 14159, left: 31, operation: '.'); } <p>Decimal Number: @strResult</p> </body> </html>
This would produce:
Overloading a Generic Method
As we have seen in previous lessons, 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 methods don't use any parameter, there would not be any name conflict. Here is an example:
public class Generator
{
private void Create()
{
}
private 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 Generator
{
private void Create(string msg)
{
}
private void Create<T>(string msg)
{
}
public string Show<T>(string msg, int value)
{
}
public string Show<FirstType, SecondType>(string msg, int value)
{
}
}
Finally, you can have methods that use different types and/or numbers of arguments. Here is an example:
public class Generator { public string Show<T>(string msg, T value) { return $"{msg}, {value}"; } public string Show<FirstType, SecondType>(FirstType first, SecondType second) { return $"First: {first}, Second: {second}"; } }
Generic Classes
Introduction to Creating a Generic Class
Like a method, 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 it is called.
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 of the class using the period operator. Inside of 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 of this operator, enter the data type that will be processed as the parameter type of the generic class. Here is an example:
@{ 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:
@{
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:
<!DOCTYPE html> <html> <head> <title>Generic Values</title> </head> <body> <h3>Generic Values</h3> @{ Generator<int> gen = new Generator<int>(); var calculation = new Generator<char>(); dynamic expression = new Generator<double>(); } <p>A Value: @gen.Value</p> <p>A Calculation: @calculation.Value</p> <p>An Expression: @expression.Value</p> </body> </html>
This would produce:
Passing a Parameter Type to a Method
We saw that you could declare a variable of a parameter type in the 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 in a browser. Here is an example:
<!DOCTYPE html> <html> <head> <title>Generic Values</title> </head> <body> <h3>Generic Values</h3> @{ var iValue = 246; Generator<int> gen = new Generator<int>(); gen.Value = iValue; } <p>Natural Number: @gen.Value</p> @{ char cValue = 'G'; var calculation = new Generator<char>(); calculation.Value = cValue; } <p>Character: @calculation.Value</p> @{ double nbr = 355.65; dynamic expression = new Generator<double>(); expression.Value = nbr; } <p>Decimal Number: @expression.Value</p> </body> </html>
This would produce:
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, 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 method. 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 the 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 methods, 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:
<!DOCTYPE html> <html> <head> <title>Generic Values</title> </head> <body> <h3>Generic Values</h3> @{ Generator<int, int> yearlySalary = new Generator<int, int>(); yearlySalary.SetTValue(84); yearlySalary.SetVValue(680); string strNumber = yearlySalary.Show(yearlySalary.GetTValue(), yearlySalary.GetVValue()); } <p>Yearly Salary: @strNumber</p> @{ Generator<char, double> price = new Generator<char, double>(); price.SetTValue('$'); price.SetVValue(185.45); string strValues = price.Show(price.GetTValue(), price.GetVValue()); } <p>Item Price: @strValues</p> @{ Generator<short, decimal> disparate = new Generator<short, decimal>(); disparate.SetTValue(425); disparate.SetVValue(.35m); strNumber = disparate.Show(disparate.GetTValue(), disparate.GetVValue()); } <p>Some Number: @strNumber</p> @{ Generator<int, string> company = new Generator<int, string>(); company.SetTValue(1800); company.SetVValue("flowers.com"); strNumber = company.Show(company.GetTValue(), company.GetVValue()); } <p>Web Site: @strNumber</p> </body> </html>
This would produce:
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 operator. Here is an example:
<!DOCTYPE html> <html> <head> <title>Generic Values</title> </head> <body> <h3>Generic Values</h3> @{ Generator<int, int> yearlySalary = new Generator<int, int>(); yearlySalary.SetTValue(84); yearlySalary.SetVValue(680); string strNumber = yearlySalary.Show(yearlySalary.GetTValue(), yearlySalary.GetVValue()); } <p>Yearly Salary: @strNumber</p> @{ Generator<char, double> price = new Generator<char, double>(); price.SetTValue('$'); price.SetVValue(185.45); string strValues = price.Show(price.GetTValue(), price.GetVValue()); } <p>Item Price: @strValues</p> @{ var disparate = new Generator<short, decimal>(); disparate.SetTValue(425); disparate.SetVValue(.35m); strNumber = disparate.Show(disparate.GetTValue(), disparate.GetVValue()); } <p>Some Number: @strNumber</p> @{ dynamic company = new Generator<int, string>(); company.SetTValue(1800); company.SetVValue("flowers.com"); strNumber = company.Show(company.GetTValue(), company.GetVValue()); } <p>Web Site: @strNumber</p> </body> </html>
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 one 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:
<!DOCTYPE html> <html> <head> <title>Generic Values</title> </head> <body> <h3>Generic Values</h3> @{ FourSideGeometricFigure sqr = new FourSideGeometricFigure(); sqr.Name = "Square"; sqr.Base = 36.82; sqr.Height = 36.82; Generator<FourSideGeometricFigure> exoSquare = new Generator<FourSideGeometricFigure>(); } <p>Summary: @exoSquare.Show(sqr)</p> @{ var rect = new FourSideGeometricFigure(); rect.Name = "Rectangle"; rect.Base = 52.94; rect.Height = 27.58; Generator<FourSideGeometricFigure> exoRect = new Generator<FourSideGeometricFigure>(); } <p>Summary: @exoRect.Show(rect)</p> </body> </html>
This would produce:
|
||
Previous | Copyright © 2008-2019, FunctionX | Next |
|