C# Methods and Functions
This chapter will detail methods (functions) in C#, including method definition, calling, parameter passing, overloading, and other core concepts, helping you write modular and reusable code.
Method Basics
Method Definition
csharp
// Basic syntax
access_modifier return_type MethodName(parameter_list)
{
// Method body
return return_value; // If return type is not void
}
// Example: Method with no parameters and no return value
public static void SayHello()
{
Console.WriteLine("Hello, World!");
}
// Example: Method with parameters and return value
public static int Add(int a, int b)
{
return a + b;
}
// Example: Method with parameters and no return value
public static void PrintMessage(string message)
{
Console.WriteLine($"Message: {message}");
}Method Calling
csharp
class Program
{
static void Main(string[] args)
{
// Call method with no parameters
SayHello();
// Call method with parameters and return value
int result = Add(5, 3);
Console.WriteLine($"5 + 3 = {result}");
// Call method with parameters and no return value
PrintMessage("This is a test message");
}
static void SayHello()
{
Console.WriteLine("Hello, World!");
}
static int Add(int a, int b)
{
return a + b;
}
static void PrintMessage(string message)
{
Console.WriteLine($"Message: {message}");
}
}Parameters
Value Parameters
csharp
// Value parameters (default)
public static void Increment(int number)
{
number++; // Only affects local copy
Console.WriteLine($"Inside method: {number}");
}
// Usage
int value = 10;
Increment(value);
Console.WriteLine($"Outside method: {value}"); // Still 10Reference Parameters (ref)
csharp
// Reference parameters
public static void Increment(ref int number)
{
number++; // Affects original variable
Console.WriteLine($"Inside method: {number}");
}
// Usage
int value = 10;
Increment(ref value); // Must use ref keyword
Console.WriteLine($"Outside method: {value}"); // Now 11Out Parameters
csharp
// Out parameters
public static bool TryParseInt(string text, out int result)
{
return int.TryParse(text, out result);
}
// Usage
string numberText = "42";
if (TryParseInt(numberText, out int number))
{
Console.WriteLine($"Successfully parsed: {number}");
}
else
{
Console.WriteLine("Failed to parse");
}
// Discard parameter (C# 7.0+)
if (int.TryParse("123", out _))
{
Console.WriteLine("Valid number");
}In Parameters (C# 7.2+)
csharp
// In parameters (read-only)
public static double CalculateArea(in double radius)
{
// radius = 10; // Error: Cannot modify in parameter
return Math.PI * radius * radius;
}
// Usage
double r = 5.0;
double area = CalculateArea(in r);Params Arrays
csharp
// Params parameter
public static int Sum(params int[] numbers)
{
int total = 0;
foreach (int num in numbers)
{
total += num;
}
return total;
}
// Usage
int sum1 = Sum(1, 2, 3, 4, 5);
int sum2 = Sum(new int[] { 10, 20, 30 });
int sum3 = Sum(); // Empty array, returns 0Method Overloading
Basic Overloading
csharp
public class Calculator
{
// Overload 1: Two integers
public static int Add(int a, int b)
{
return a + b;
}
// Overload 2: Three integers
public static int Add(int a, int b, int c)
{
return a + b + c;
}
// Overload 3: Two doubles
public static double Add(double a, double b)
{
return a + b;
}
// Overload 4: Array of integers
public static int Add(params int[] numbers)
{
int sum = 0;
foreach (int num in numbers)
sum += num;
return sum;
}
}
// Usage
Console.WriteLine(Calculator.Add(5, 3)); // Calls overload 1
Console.WriteLine(Calculator.Add(1, 2, 3)); // Calls overload 2
Console.WriteLine(Calculator.Add(1.5, 2.5)); // Calls overload 3
Console.WriteLine(Calculator.Add(1, 2, 3, 4)); // Calls overload 4Overloading with Optional Parameters
csharp
public class Greeter
{
// Method with optional parameters
public static void Greet(string name, string greeting = "Hello")
{
Console.WriteLine($"{greeting}, {name}!");
}
// Overload with different signature
public static void Greet()
{
Greet("Guest", "Welcome");
}
}
// Usage
Greeter.Greet("Alice"); // Uses default greeting
Greeter.Greet("Bob", "Hi"); // Custom greeting
Greeter.Greet(); // Calls overloadReturn Values
Single Return Value
csharp
public static int Divide(int a, int b)
{
if (b == 0)
return 0; // Return default value for error case
return a / b;
}Multiple Return Values
Using Tuple (C# 7.0+)
csharp
public static (int quotient, int remainder) DivideWithRemainder(int a, int b)
{
int quotient = a / b;
int remainder = a % b;
return (quotient, remainder);
}
// Usage
var (q, r) = DivideWithRemainder(17, 5);
Console.WriteLine($"Quotient: {q}, Remainder: {r}");Using out Parameters
csharp
public static bool DivideWithRemainder(int a, int b, out int quotient, out int remainder)
{
if (b == 0)
return false;
quotient = a / b;
remainder = a % b;
return true;
}
// Usage
if (DivideWithRemainder(17, 5, out int q, out int r))
{
Console.WriteLine($"Quotient: {q}, Remainder: {r}");
}Local Functions (C# 7.0+)
Basic Local Functions
csharp
public static void ProcessData()
{
// Local function
bool IsValid(int number)
{
return number > 0 && number < 100;
}
for (int i = 0; i < 10; i++)
{
if (IsValid(i))
Console.WriteLine($"Valid: {i}");
}
}Local Functions with Closures
csharp
public static List<int> FilterNumbers(List<int> numbers, Func<int, bool> predicate)
{
var result = new List<int>();
foreach (int num in numbers)
{
// Local function with closure
bool ShouldInclude(int n)
{
return predicate(n) && n % 2 == 0;
}
if (ShouldInclude(num))
result.Add(num);
}
return result;
}
// Usage
var numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var evenNumbers = FilterNumbers(numbers, n => n > 5);Expression-Bodied Members
Expression-Bodied Methods
csharp
public class MathUtils
{
// Traditional method
public static int AddTraditional(int a, int b)
{
return a + b;
}
// Expression-bodied method
public static int AddExpression(int a, int b) => a + b;
// Expression-bodied method with logic
public static bool IsEven(int number) => number % 2 == 0;
// Expression-bodied method with multiple statements
public static string GetGreeting(string name) =>
string.IsNullOrEmpty(name) ? "Hello!" : $"Hello, {name}!";
}Recursive Methods
Basic Recursion
csharp
public static int Factorial(int n)
{
// Base case
if (n <= 1)
return 1;
// Recursive case
return n * Factorial(n - 1);
}
// Usage
Console.WriteLine($"5! = {Factorial(5)}"); // 120Tail Recursion
csharp
public static int FactorialTail(int n, int accumulator = 1)
{
if (n <= 1)
return accumulator;
return FactorialTail(n - 1, n * accumulator);
}
// Usage
Console.WriteLine($"5! = {FactorialTail(5)}"); // 120Method Modifiers
Static Methods
csharp
public class MathHelper
{
public static double PI = 3.14159;
public static double CalculateCircleArea(double radius)
{
return PI * radius * radius;
}
public void InstanceMethod()
{
Console.WriteLine("This is an instance method");
}
}
// Usage
double area = MathHelper.CalculateCircleArea(5.0);
// MathHelper helper = new MathHelper();
// helper.InstanceMethod(); // Requires instanceExtension Methods
csharp
// Extension method must be in static class
public static class StringExtensions
{
public static bool IsNullOrEmpty(this string str)
{
return string.IsNullOrEmpty(str);
}
public static string Reverse(this string str)
{
if (string.IsNullOrEmpty(str))
return str;
char[] chars = str.ToCharArray();
Array.Reverse(chars);
return new string(chars);
}
}
// Usage
string text = "Hello";
bool isEmpty = text.IsNullOrEmpty(); // Extension method
string reversed = text.Reverse(); // Extension methodGeneric Methods
Basic Generic Methods
csharp
public static T Max<T>(T a, T b) where T : IComparable<T>
{
return a.CompareTo(b) > 0 ? a : b;
}
// Usage
int maxInt = Max(5, 10); // T is int
double maxDouble = Max(3.14, 2.71); // T is double
string maxString = Max("Apple", "Banana"); // T is stringGeneric Methods with Constraints
csharp
public static void PrintCollection<T>(IEnumerable<T> collection) where T : class
{
Console.WriteLine($"Collection type: {typeof(T).Name}");
foreach (T item in collection)
{
Console.WriteLine(item?.ToString() ?? "null");
}
}
// Usage
var numbers = new List<int> { 1, 2, 3 };
PrintCollection(numbers);
var strings = new List<string> { "A", "B", "C" };
PrintCollection(strings);Practical Examples
String Utilities
csharp
public static class StringUtils
{
// Count words in a string
public static int CountWords(this string text)
{
if (string.IsNullOrWhiteSpace(text))
return 0;
return text.Split(new[] { ' ', '\t', '\n' },
StringSplitOptions.RemoveEmptyEntries).Length;
}
// Capitalize first letter
public static string Capitalize(this string text)
{
if (string.IsNullOrEmpty(text))
return text;
return char.ToUpper(text[0]) + text.Substring(1);
}
// Truncate with ellipsis
public static string Truncate(this string text, int maxLength)
{
if (string.IsNullOrEmpty(text) || text.Length <= maxLength)
return text;
return text.Substring(0, maxLength - 3) + "...";
}
}Array Utilities
csharp
public static class ArrayUtils
{
// Find maximum in array
public static T FindMax<T>(T[] array) where T : IComparable<T>
{
if (array == null || array.Length == 0)
throw new ArgumentException("Array cannot be null or empty");
T max = array[0];
foreach (T item in array)
{
if (item.CompareTo(max) > 0)
max = item;
}
return max;
}
// Shuffle array
public static void Shuffle<T>(T[] array)
{
Random random = new Random();
for (int i = 0; i < array.Length; i++)
{
int j = random.Next(i, array.Length);
(array[i], array[j]) = (array[j], array[i]); // Tuple deconstruction
}
}
// Check if array contains element
public static bool Contains<T>(T[] array, T item)
{
foreach (T element in array)
{
if (EqualityComparer<T>.Default.Equals(element, item))
return true;
}
return false;
}
}Validation Utilities
csharp
public static class ValidationUtils
{
// Validate email format
public static bool IsValidEmail(string email)
{
if (string.IsNullOrWhiteSpace(email))
return false;
try
{
var addr = new System.Net.Mail.MailAddress(email);
return addr.Address == email;
}
catch
{
return false;
}
}
// Validate phone number
public static bool IsValidPhoneNumber(string phone)
{
if (string.IsNullOrWhiteSpace(phone))
return false;
// Remove common formatting
string cleaned = System.Text.RegularExpressions.Regex.Replace(phone, @"[^\d]", "");
// Check if it's 10 digits (US format)
return cleaned.Length == 10;
}
// Validate age range
public static bool IsValidAge(int age, int min = 0, int max = 150)
{
return age >= min && age <= max;
}
}Best Practices
Method Design Principles
csharp
// Good: Single responsibility
public static bool IsValidEmail(string email)
{
// Only validates email format
}
// Good: Descriptive names
public static double CalculateCircleArea(double radius)
{
// Clear purpose and parameters
}
// Good: Proper error handling
public static int SafeParseInt(string text, int defaultValue = 0)
{
return int.TryParse(text, out int result) ? result : defaultValue;
}Parameter Validation
csharp
public static decimal CalculateDiscount(decimal price, decimal discountPercentage)
{
// Validate parameters
if (price < 0)
throw new ArgumentException("Price cannot be negative");
if (discountPercentage < 0 || discountPercentage > 100)
throw new ArgumentException("Discount percentage must be between 0 and 100");
return price * (1 - discountPercentage / 100);
}Documentation
csharp
/// <summary>
/// Calculates the compound interest over a period of time.
/// </summary>
/// <param name="principal">The initial amount of money</param>
/// <param name="rate">The annual interest rate (as decimal, e.g., 0.05 for 5%)</param>
/// <param name="years">The number of years</param>
/// <param name="compoundsPerYear">Number of times interest is compounded per year</param>
/// <returns>The final amount after compound interest</returns>
/// <example>
/// <code>
/// double final = CalculateCompoundInterest(1000, 0.05, 10, 12);
/// </code>
/// </example>
public static double CalculateCompoundInterest(
decimal principal,
decimal rate,
int years,
int compoundsPerYear = 1)
{
return principal * (decimal)Math.Pow(
1.0 + (double)(rate / compoundsPerYear),
years * compoundsPerYear);
}Summary
In this chapter, you learned:
- Method definition and calling
- Parameter types: value, ref, out, in, params
- Method overloading
- Return values and multiple returns
- Local functions and expression-bodied members
- Recursive methods
- Method modifiers: static, extension
- Generic methods
- Practical examples and best practices
Methods are fundamental building blocks of C# programs, enabling code reuse, organization, and maintainability. In the next chapter, we'll explore arrays and collections for handling multiple data items efficiently.