Method Group in C#
- Delegates in C#
- Method Groups in C#
- Method Groups Conversion With Delegate in C#
- Simple Custom Delegate for Method Groups in C#
- Use Cases and Benefits
- Binding Method Groups to Delegates
- Delegate Invocation with Method Groups
- Conclusion
In C#, a method group is a fundamental concept that facilitates delegate binding, aiding in the creation of more flexible and dynamic code. Method groups serve as a reference to one or more methods with a compatible signature, enabling a simplified way to handle functions as objects.
This article aims to delve into the nuances of method groups, their syntax, usage, and significance within the C# programming language.
Delegates in C#
Before diving into method groups, it’s crucial to grasp the essence of delegates. Delegates in C# act as type-safe function pointers, allowing the invocation of methods indirectly.
They provide a way to treat methods as entities that can be assigned, passed as parameters, and invoked dynamically.
Delegates are pointers to functions created. Along with pointing to the function, it also defines the return type of the functions and their parameters.
A delegate, let’s say, called LAPTOP
, can have a return type as void
and a parameter as USERNAME
(string). We have another function called PC
, which also has the same return type and parameter type as LAPTOP
.
We can call PC
from the delegate LAPTOP
in this scenario.
Code Example:
using System;
namespace ConsoleApplication1 {
class Program {
delegate void LAPTOP(string username);
static void PC(string username) {
Console.WriteLine("USER: " + username + " has logged in!");
}
static void Main() {
LAPTOP lap1 = PC;
lap1("Hydrogen");
}
}
}
You can notice that we have instantiated the LAPTOP
instance with PC
, and invoking it later causes us to print the username passed from the PC
method. That is how delegates work.
Method Groups in C#
We sometimes encounter a case where a function may have more than one implementation. It might have another definition with an entirely different parameter set or type or is usually known as an overloaded function.
Syntax:
void print(int x) {
///...code
}
void print(int x, int y) {
///...code for x and y
}
Such functions or methods are called method groups.
A method group represents a set of methods that have the same name and compatible signatures. This group of methods can be referred to collectively by the delegate, simplifying the invocation of these methods through the delegate instance.
The syntax for referencing a method group involves specifying the method name without parentheses or arguments. For instance:
// Define a delegate
delegate int MyDelegate(int x, int y);
// Methods compatible with the delegate signature
int Add(int a, int b) => a + b;
int Multiply(int a, int b) => a * b;
// Creating a method group
MyDelegate delegateInstance1 = Add;
MyDelegate delegateInstance2 = Multiply;
In this code, Add
and Multiply
are methods compatible with the MyDelegate
delegate signature. The method group creation involves assigning these methods to delegate instances without invoking them, allowing the delegate to reference them.
Method Groups Conversion With Delegate in C#
We’ll look at one of the most common uses of method groups and how to handle them when called. We discussed how method groups are overloaded functions of a basic method, and we can use method groups as we desire by using delegates.
In our example, let’s suppose that we have a method called CAR
from which we want to address several other methods, such as DASHBOARD
, WHEEL
, LIGHTS
etc. We will, for convenience, only use two methods; LIGHTS
and MIRRORS
.
Code Example:
delegate void CAR(bool start);
static void MIRROR(bool start) {
if (start)
Console.WriteLine("Mirror is now working!");
else
Console.WriteLine("Mirror is not stopped/closed");
}
static void LIGHTS(bool start) {
if (start)
Console.WriteLine("Lights are now working!");
else
Console.WriteLine("Lights have stopped/closed");
}
The code demonstrates the use of a delegate CAR
to reference methods (MIRROR
and LIGHTS
) with matching signatures. Method groups are created by assigning these methods to delegate instances, allowing for the indirect invocation of methods based on the delegate instances and provided arguments.
This abstraction enables more flexible and dynamic handling of method calls, particularly in scenarios where different methods need to be invoked based on certain conditions or events, as seen with the car-related functionalities of mirrors and lights in this example.
Now that we have defined the methods, let’s implement the MAIN
method, where we use delegates to handle these method groups.
CAR car = MIRROR;
car(true);
car = LIGHTS;
car(false);
You can notice the CAR
object points to the MIRROR
, but later it is made to point to LIGHTS
. Then, calling the method will call the function that it points to.
The function’s name is assigned to the car
object. Changing the DELEGATE
pointer to point at different methods in its group is called METHOD GROUP CONVERSION
, and in this scenario, the LIGHTS
, MIRRORS
, and CAR
are all part of the same method group.
Full code:
using System;
namespace ConsoleApplication1 {
class Program {
delegate void CAR(bool start);
static void MIRROR(bool start) {
if (start)
Console.WriteLine("Mirror is now working!");
else
Console.WriteLine("Mirror is not stopped/closed");
}
static void LIGHTS(bool start) {
if (start)
Console.WriteLine("Lights are now working!");
else
Console.WriteLine("Lights have stopped/closed");
}
static void Main() {
CAR car = MIRROR;
car(true);
car = LIGHTS;
car(false);
}
}
}
Output:
Mirror is now working!
Lights have stopped/closed
Simple Custom Delegate for Method Groups in C#
A simple way to create a delegate that can point to method groups is something as simple as follows.
Func<string> f_test = "AA".ToString;
Console.WriteLine(f_test());
Invoking the above will output the string AA
as a result. f_test
points to the ToString
method and calls it.
You can also notice that this function points to the address of the ToString
method and not the function itself. That is how pointers work.
Another example has been provided below to understand method groups properly.
Func<string, int> f_test = Convert.ToInt32;
Console.WriteLine(f_test("435"));
Syntax:
Func<param1, param2> name;
// param1 is the parameter passed in the function/method pointed to
// param 2 is the return type of that function/method that the delegate points to
As Convert.ToInt32
has a return type of INT
and 18
different overloads. It is imperative to define the parameter type because we want to call f_test
with a string "435"
, and we define param2
as STRING
.
Defining param2
is important even if there is just one overload of a function present; because method groups are compiled time constructs, they must be chosen for a single overload. It is important to ensure that param2
contains at least one overload.
You can also remove the cast in LINQ
when calling the List.Select(MethodGroup)
in a function. We will not discuss LINQ
contexts in detail because this article focuses on method groups.
Use Function Pointers for Method Groups in C#
Pointing to different functions from a delegate is only needed if you are working in method groups. You must ensure that the return types and parameters match in such circumstances.
C# already provides a FUNC
keyword to allow for pointing to different functions. Just as pointers work by pointing to addresses of objects and variables and then calling them, you can imagine delegates the same way.
Pointers also tend to have the same return type as the objects they address. Delegates also have to take care of parameters due to function requirements.
Pointing to functions is needed when, let’s say, you want to store functions in an array and call them dynamically for your code or pass the functions to other functions to be called. LAMBDA
functions serve the same requirement as well.
However, method groups are common clusters with overloaded functions that follow a basic return and parameter type.
Use Cases and Benefits
Method groups offer several advantages and use cases:
- Callback Mechanisms: They are widely used in scenarios where callbacks are needed, like event handling or asynchronous programming.
- Code Flexibility: Method groups enhance code flexibility by allowing dynamic invocation of methods at runtime, thus enabling more adaptable and modular code structures.
- Encapsulation: They aid in encapsulation by allowing a delegate to reference multiple methods, providing a level of abstraction.
Binding Method Groups to Delegates
The binding of a method group to a delegate involves assigning the method group to a delegate instance of a compatible signature. This can be achieved using the delegate keyword or through the use of lambda expressions:
MyDelegate delegateInstance = Add; // Binding method group using delegate keyword
// Using lambda expression
MyDelegate delegateInstanceLambda = (a, b) => Multiply(a, b);
Delegate Invocation with Method Groups
Once a method group is bound to a delegate, invoking the delegate will call all the methods referenced by the method group. For example:
int result = delegateInstance(2, 3); // Invokes the Add method
int resultLambda = delegateInstanceLambda(2, 3); // Invokes the Multiply method
Conclusion
Method groups play a pivotal role in enhancing the flexibility, modularity, and abstraction of C# code. They enable the creation of dynamic and adaptable systems by providing a way to reference multiple methods with compatible signatures through a single delegate instance.
Understanding method groups is essential for leveraging the power of delegates and harnessing the dynamic nature of C# programming, enabling the development of more robust and scalable applications.
Hello, I am Bilal, a research enthusiast who tends to break and make code from scratch. I dwell deep into the latest issues faced by the developer community and provide answers and different solutions. Apart from that, I am just another normal developer with a laptop, a mug of coffee, some biscuits and a thick spectacle!
GitHub