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:

@functions{
    // Use an integer as parameter
    void Show(int value)
    {
        System.Console.WriteLine("Natural Number: {0}", value);
    }

    // Use a double-precision value as parameter
    void Show(double value)
    {
        System.Console.WriteLine("Floating - Point Number: {0}", value);
    }

    // Use a character as parameter
    void Show(string value)
    {
        System.Console.WriteLine("String: {0}", value);
    }
}

You can then call the functions. Here are examples:

@page
@model Exercises.Pages.ExerciseModel
@using static System.Console
@{
    // 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);
}

@functions{
    // Use an integer as parameter
    void Show(int value)
    {
        WriteLine("Natural Number: {0}", value);
    }

    // Use a double-precision value as parameter
    void Show(double value)
    {
        WriteLine("Floating - Point Number: {0}", value);
    }

    // Use a character as parameter
    void Show(string value)
    {
        WriteLine("String: {0}", value);
    }
}

This would produce:

Natural Number: 246
String: G
Floating - Point Number: 355.65

We passed a constant value directly to the function when we called it. Remember that you can also first declare a variable, assign it a value, and then pass that variable to the function. Here are examples:

@page
@model Valuable.Pages.GenericsModel
@{
    int value1 = 246;
    string 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);    
}

@functions{
    // Use an integer as parameter
    void Show(int value)
    {
        System.Console.WriteLine("Natural Number: {0}", value);
    }

    // Use a double-precision value as parameter
    void Show(double value)
    {
        System.Console.WriteLine("Floating - Point Number: {0}", value);
    }

    // Use a character as parameter
    void Show(string value)
    {
        System.Console.WriteLine("String: {0}", value);
    }
}

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 value 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:

@functions{
    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:

@functions{
    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. To do this, after the name of the parameter, type .ToString(). Here is an example:

@functions{
    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:

@functions{
    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:

@page
@model Valuable.Pages.GenericsModel
@{
    int value1 = 246;
    string 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);   
}

@functions{
    void Show<T>(T value)
    {
        System.Console.WriteLine("Value: {0}", value.ToString());
    }
}

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:

@page
@model Valuable.Pages.GenericsModel
@{
    int value1 = 927_149;
    string value2 = "Wonderful";
    double value3 = 484_285.77;

    // Call the function that takes an integer as argument
    Show<int>(value1);

    // Call the version of the function that takes a character as argument
    Show<string>(value2);

    // Call the version of the function that takes a decimal as argument
    Show<double>(value3);    
}

@functions{
    // Use an integer as parameter
    void Show<T>(T value)
    {
        System.Console.WriteLine("Value: {0}", value.ToString());
    }
}

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:

@page
@model Valuable.Pages.GenericsModel
@{   
}

@functions{
    // 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(string value)
    {
        return value;
    }
}

<p>Calling the function that takes an integer as argument</p>
<p><b>Natural Number: @Show(583_749)</b></p>

<p>Calling the version of the function that takes a character as argument</p>
<p><b>String: @Show("Good")</b></p>

<p>Calling the version of the function that takes a decimal as argument</p>
<p><b>Floating - Point Number: @Show(863_605.747)</b></p>

This would produce::

Calling the function that takes an integer as argument

Natural Number: 583749

Calling the version of the function that takes a character as argument

String: Good

Calling the version of the function that takes a decimal as argument

Floating - Point Number: 863605.747

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:

@page
@model Valuable.Pages.GenericsModel
@{
    string Show<T>(T value)
    {
        return value.ToString();
    }
}

<p>Calling the function that takes an integer as argument</p>
<p><b>Natural Number: @Show(583_749)</b></p>

<p>Calling the version of the function that takes a character as argument</p>
<p><b>String: @Show("Good")</b></p>

<p>Calling the version of the function that takes a decimal as argument</p>
<p><b>Floating - Point Number: @Show(863_605.77)</b></p>

Remember that, when calling the function, you can include the type of the generic value. Here are examples:

@page
@model Valuable.Pages.GenericsModel
@{
    string Show<T>(T value)
    {
        return value.ToString();
    }

    string i = Show<int>(279_487);
    string d = Show<double>(827_504.803_26);
    string s = Show<string>("Wonderful World");
}

<p>Calling the function that takes an integer as argument</p>
<p><b>Natural Number: @i</b></p>

<p>Calling the version of the function that takes a character as argument</p>
<p><b>String: @s</b></p>

<p>Calling the version of the function that takes a decimal as argument</p>
<p><b>Floating - Point Number: @d</b></p>

This would produce:

Calling the function that takes an integer as argument

Natural Number: 279487

Calling the version of the function that takes a character as argument

String: Wonderful World

Calling the version of the function that takes a decimal as argument

Floating - Point Number: 827504.80326

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:

@page
@model Valuable.Pages.GenericsModel
@using static System.Console
@{
    void Show<T>(string msg, T value)
    {
        WriteLine($"{msg}: {value}");
    }

    int value1 = 5_268;
    string value2 = "t";
    double value3 = 7_294_570.71539;

    // Call the version that takes an integer as argument
    Show<int>("Integer", value1);

    // Call the version that takes a character as argument
    Show<string>("Character", value2);

    // Call the version that takes a decimal number as argument
    Show<double>("Decimal", value3);
}

This would produce:

Integer: 5268
Character: t
Decimal: 7294570.71539

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:

@page
@model Valuable.Pages.GenericsModel
@using static System.Console
@{
    void Show<T>(string msg, T value)
    {
        WriteLine($"{msg}: {value}");
    }

    const int iValue = 5_268;
    var message = "Integer";
    Show(message, iValue);
    
    message = "Character";
    const string sValue = "t";
    Show(message, sValue);
    
    message = "Decimal";
    const double dValue = 7_294_570.71539;
    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:

@functions{
    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:

@functions{
    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:

@functions{
    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:

@functions{
    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:

@page
@model Valuable.Pages.GenericsModel
@using static System.Console
@{
    void Show<U, V>(U first, V second)
    {
        WriteLine($"First: {first}; Second: {second}");
    }

    const int iValue = 9;
    var message = "Integer";
    Show(message, iValue);
    
    message = "Character";
    const string sValue = "@";
    Show(message, sValue);
    
    message = "Decimal";
    const double dValue = 827_838_575.694683;
    Show(message, dValue);
}

This would produce:

First: Integer; Second: 9
First: Character; Second: @
First: Decimal; Second: 827838575.694683

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:

@page
@model Valuable.Pages.GenericsModel
@{
    void Show<U, V>(U first, V second)
    {
        System.Console.WriteLine($"First: {first}; Second: {second}");
    }

    const int iValue = 9;
    var message = "Integer";
    Show<string, int>(message, iValue);
    
    message = "Character";
    const string sValue = "@";
    Show<string, string>(message, sValue);
    
    message = "Decimal";
    const double dValue = 827_838_575.694683;
    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:

@page
@model Valuable.Pages.GenericsModel
@{
    string Show<T>(string msg, T value)
    {
        return $"{msg}: {value}";
    }

    var number = Show<int>("Integer", 493_840);
    dynamic character = Show<string>("Character", "$");
    var floating = Show<double>("Decimal", 795_470.365);
}

<h3>Generics</h3>

<p>@number</p>
<p>@character</p>
<p>@floating</p>

This would produce:

Generics

Integer: 493840

Character: $

Decimal: 795470.365

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. 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:

@page
@model Valuable.Pages.GenericsModel
@{
    string Show<T>(T value, string msg = "Some Value")
    {
        return $"{msg}: {value}";
    }

    var number = Show<int>(493_840);
    dynamic character = Show<string>("$");
    var floating = Show<double>(795_470.365);
}

<h3>Generics</h3>

<p>@number</p>
<p>@character</p>
<p>@floating</p>

This would produce:

Generics

Some Value: 493840

Some Value: $

Some Value: 795470.365

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:

@page
@model Valuable.Pages.GenericsModel
@{
   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: ".");   
}

@functions{
    string Evaluate<T>(T right, T left, string operation)     
    {
        return $"{left} {operation} {right}";    
    }
}

<h3>Generic Values</h3>

<hr />

<p>Operation: @strFirstResult</p>

<p>Expression: @strSecondResult</p>

<p>Decimal Number: @strThirdResult</p>

This would produce:

Generic Values

Operation: 797.24 + 20.64

Expression: Research & Development

Decimal Number: 31 . 14159

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:

@functions{
    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:

@functions{
    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:

@functions{
    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

In previous sections, we learned to create generic functions. Everything we learned about generic functions is directly applicable to the methods of a class, as generic methods. This means that we can create a generic function as a member of a class, making it a generic method. Here is an example:

@functions{
    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:

@page
@model Valuable.Pages.GenericsModel
@{
    int value1 = 246;
    string value2 = "Good";       
    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);
}

@functions{
    public class Generator
    {
        public void Show<T>(T value)
        {
            System.Console.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:

@functions{
    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:

@functions{
    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:

@functions{
    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:

@{
    Generator<int> gen = new Generator<int>();
}

@functions{
    public class Generator<TypeOfValue>
    {
        public TypeOfValue Value;

        public string Show()
        {
            return Value.ToString();
        }
    }
}

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>();
}

@functions{
    public class Generator<TypeOfValue>
    {
        public TypeOfValue Value;

        public string Show()
        {
            return Value.ToString();
        }
    }
}

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:

@page
@model Valuable.Pages.GenericsModel
@{
    Generator<int> gen = new Generator<int>();
    var calculation = new Generator<int>();
    dynamic expression = new Generator<int>();
}

@functions{
    public class Generator<TypeOfValue>
    {
        public TypeOfValue Value;

        public string Show()
        {
            return Value.ToString();
        }
    }
}

<pre>Generic Values
------------------------------------
A Value: @gen.Value
A Calculation: @calculation.Value
An Expression: @expression.Value
=======================================</pre>

This would produce:

Generic Values
------------------------------------
A Value: 0
A Calculation:
An Expression: 0
=======================================

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 as 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:

@page
@model Valuable.Pages.GenericsModel
@{
    var iValue = 246;
    Generator<int> gen = new Generator<int>();

    gen.Show(iValue);

    string sValue = "Wonderful";
    var calculation = new Generator<string>();        
    calculation.Show(sValue);

    double nbr = 355.65;
    dynamic expression = new Generator<double>();
    expression.Show(nbr);
}

@functions{
    public class Generator<TypeOfValue>
    {
        public void Show(TypeOfValue value)
        {
            System.Console.WriteLine("Value: {0}", value);
        }
    }
}

This would produce:

Value: 246
Value: Wonderful
Value: 355.65

In the same way, you can pass the parameter type to a constructor of the class. Here is an example:

@functions{
    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:

@functions{
    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:

@functions{
    public class Generator<T>
    {
        public T Value;

        public Generator(T value)
        {
            Value = value;
        }

        private T Create()
        {
            return Value;
        }
    }
}

Generics and Properties

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:

@functions{
    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:

@functions{
    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:

@page
@model Valuable.Pages.GenericsModel
@{
    Generator<int, int> yearlySalary = new Generator<int, int>();
    yearlySalary.SetTValue(84);
    yearlySalary.SetVValue(680);
    string strYearlySalary = yearlySalary.Show(yearlySalary.GetTValue(), yearlySalary.GetVValue());
    
    Generator<string, double> price = new Generator<string, double>();
    price.SetTValue("$");
    price.SetVValue(185.45);
    string strPrice = price.Show(price.GetTValue(), price.GetVValue());
            
    Generator<int, double> disparate = new Generator<int, double>();
    disparate.SetTValue(425);
    disparate.SetVValue(.35m);
    string strValue = disparate.Show(disparate.GetTValue(), disparate.GetVValue());
 
    Generator<int, string> company = new Generator<int, string>();
    company.SetTValue(1800);
    company.SetVValue("flowerslovers.com");
    string strAddress = company.Show(company.GetTValue(), company.GetVValue());
}

@functions{
    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}";
        }
    }
}

<pre>Generic Values
--------------------------------------
Yearly Salary: @strYearlySalary
Item Price: @strPrice
Some Number: @strValue
Web Site: @strAddress
======================================</pre>

This would produce:

Generic Values
--------------------------------------
Yearly Salary: 84680
Item Price: $185.45
Some Number: 4250.35
Web Site: 1800flowerslovers.com
======================================

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:

@page
@model Valuable.Pages.GenericsModel
@{
    Generator<int, int> yearlySalary = new Generator<int, int>();
    yearlySalary.SetTValue(84);
    yearlySalary.SetVValue(680);
    var strYearlySalary = yearlySalary.Show(yearlySalary.GetTValue(), yearlySalary.GetVValue());
    
    var price = new Generator<string, double>();
    price.SetTValue("$");
    price.SetVValue(185.45);
    string strPrice = price.Show(price.GetTValue(), price.GetVValue());

    var disparate = new Generator<short, decimal>();
    disparate.SetTValue(425);
    disparate.SetVValue(.35m);
    var strValue = disparate.Show(disparate.GetTValue(), disparate.GetVValue());
 
    dynamic company = new Generator<int, string>();
    company.SetTValue(1800);
    company.SetVValue("flowerslovers.com");
    string strAddress = company.Show(company.GetTValue(), company.GetVValue());
}

@functions{
    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}";
    }
}

<pre>Generic Values
--------------------------------------
Yearly Salary: @strYearlySalary
Item Price: @strPrice
Some Number: @strValue
Web Site: @strAddress
======================================</pre>

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 library. 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:

@functions{
    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:

@functions{
    public class FourSideGeometricFigure
    {
        public string name;
        public double @basis;
        public double height;
        public double CalculateArea()
        {
            return basis * height;
        }
        
        public string Display()
        {
            return $"Type:  {name}, Base: {@base}, Height: {height}, Area: {CalculateArea()}";
        }
    }
    
    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:

@page
@model Valuable.Pages.GenericsModel
@{
    FourSideGeometricFigure sqr = new FourSideGeometricFigure();

    sqr.name   = "Square";
    sqr.basis  = 36.82;
    sqr.height = 36.82;
    Generator<FourSideGeometricFigure> exoSquare =
                    new Generator<FourSideGeometricFigure>();
                  
    var rect = new FourSideGeometricFigure();
    
    rect.name   = "Rectangle";
    rect.basis  = 52.94;
    rect.height = 27.58;
    
    dynamic exoRect = new Generator<FourSideGeometricFigure>();
}

@functions{
    public class FourSideGeometricFigure
    {
        public string name;
        public double basis;
        public double height;

        public double CalculateArea()
        {
            return basis * height;
        }
    }
    
    public class Generator<T>
    {
        public string Show(T val)
        {
            return val.ToString();
        }
    }
}

Previous Copyright © 2008-2022, FunctionX Tuesday 19 October 2021 Next