7 Powerful Ways to Pass Parameters Among Classes in C#
Techniques for Cleaner and Flexible Class Interactions
Passing parameters among classes is a fundamental aspect of object-oriented programming in C#. Whether it's passing configuration values, dependencies, or data, the method you choose can significantly impact your code’s readability, maintainability, and scalability.
In this blog, we’ll explore various ways to pass parameters among classes, discussing their advantages, use cases, and examples in C#.
1. Constructor Injection
What It Is:
Constructor injection involves passing parameters or dependencies through a class’s constructor when creating an instance. It’s a core principle of dependency injection.
Advantages:
Ensures dependencies are available at initialization.
Encourages immutability.
Simplifies testing and mocking.
Example:
👋 Become 1% better at .NET Full Stack development every day.
👆 https://dotnet-fullstack-dev.blogspot.com/
â™» Restack it Vibe matches, help others to get it
public class Logger
{
public void Log(string message)
{
Console.WriteLine(message);
}
}
public class OrderService
{
private readonly Logger _logger;
public OrderService(Logger logger)
{
_logger = logger;
}
public void ProcessOrder(string order)
{
_logger.Log($"Processing order: {order}");
}
}
class Program
{
static void Main()
{
var logger = new Logger();
var orderService = new OrderService(logger);
orderService.ProcessOrder("Order123");
}
}
2. Method Parameters
What It Is:
Pass parameters directly to a method when calling it. This approach is simple and intuitive for one-off values or data that’s not reused frequently.
Advantages:
Flexible for one-off use.
Keeps methods isolated from external dependencies.
Example:
public class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
}
class Program
{
static void Main()
{
var calculator = new Calculator();
var result = calculator.Add(5, 10);
Console.WriteLine($"Result: {result}");
}
}
3. Properties (Setter Injection)
What It Is:
Pass parameters by setting properties on a class instance. This is useful when the parameters might change after initialization or are optional.
Advantages:
Allows optional or delayed parameter setting.
Useful for configurations.
Example:
public class Printer
{
public string Prefix { get; set; }
public void Print(string message)
{
Console.WriteLine($"{Prefix}: {message}");
}
}
class Program
{
static void Main()
{
var printer = new Printer { Prefix = "INFO" };
printer.Print("Hello, world!");
}
}
4. Passing Parameters via Delegates
What It Is:
Use delegates or lambda expressions to pass functionality or parameters between classes. This is ideal for scenarios requiring flexibility or callbacks.
Advantages:
Enables behavior injection.
Highly flexible for event-driven programming.
Example:
public class TaskExecutor
{
public void ExecuteTask(Action task)
{
task();
}
}
class Program
{
static void Main()
{
var executor = new TaskExecutor();
executor.ExecuteTask(() => Console.WriteLine("Task executed!"));
}
}
5. Using Interfaces
What It Is:
Pass dependencies as interfaces instead of concrete implementations. This promotes loose coupling and adheres to the Dependency Inversion Principle.
Advantages:
Enhances testability.
Reduces coupling between classes.
Example:
public interface ILogger
{
void Log(string message);
}
public class ConsoleLogger : ILogger
{
public void Log(string message)
{
Console.WriteLine(message);
}
}
public class NotificationService
{
private readonly ILogger _logger;
public NotificationService(ILogger logger)
{
_logger = logger;
}
public void Notify(string message)
{
_logger.Log($"Notification: {message}");
}
}
class Program
{
static void Main()
{
ILogger logger = new ConsoleLogger();
var notificationService = new NotificationService(logger);
notificationService.Notify("You have a new message!");
}
}
6. Using Context Classes
What It Is:
Pass a context object that encapsulates multiple parameters or state. This simplifies method signatures when multiple parameters need to be passed together.
Advantages:
Reduces parameter clutter in method calls.
Encapsulates related data.
Example:
public class RequestContext
{
public string UserId { get; set; }
public string SessionId { get; set; }
}
public class RequestHandler
{
public void HandleRequest(RequestContext context)
{
Console.WriteLine($"Handling request for User: {context.UserId}, Session: {context.SessionId}");
}
}
class Program
{
static void Main()
{
var context = new RequestContext { UserId = "User123", SessionId = "Session456" };
var handler = new RequestHandler();
handler.HandleRequest(context);
}
}
7. Event-Driven Communication
What It Is:
Use events to pass parameters indirectly when something happens. This is useful for decoupling event producers and consumers.
Advantages:
Decouples classes.
Encourages reactive programming.
Example:
public class Publisher
{
public event Action<string> OnMessagePublished;
public void PublishMessage(string message)
{
OnMessagePublished?.Invoke(message);
}
}
public class Subscriber
{
public void Subscribe(Publisher publisher)
{
publisher.OnMessagePublished += HandleMessage;
}
private void HandleMessage(string message)
{
Console.WriteLine($"Received message: {message}");
}
}
class Program
{
static void Main()
{
var publisher = new Publisher();
var subscriber = new Subscriber();
subscriber.Subscribe(publisher);
publisher.PublishMessage("Hello, subscribers!");
}
}
Comparison of Approaches
Conclusion
Choosing the right way to pass parameters depends on your specific use case, the level of coupling required, and the complexity of your application. Whether you’re focusing on simplicity, flexibility, or testability, understanding these approaches will help you design better systems.
Have you explored these parameter-passing techniques? Which one do you find most effective? Let us know in the comments! 😊