Unit 3: Advanced C# Programming
I. Orientation
Advanced C# programming combines expressive language features with design principles for building reusable, maintainable, and responsive .NET applications. LINQ simplifies data querying, delegates and events support flexible communication, interfaces and generics promote abstraction and reuse, and asynchronous programming keeps applications responsive. The SOLID principles provide a design framework for controlling dependency and change.
- Type safety: The compiler checks types such as
int,string, interfaces, and generic parameters before execution. - Abstraction: Interfaces and delegates expose required behavior without forcing callers to know implementation details.
- Deferred execution: Many LINQ queries execute only when their results are enumerated.
- Loose coupling: Dependencies should be represented by abstractions, allowing implementations to change independently.
- Resource awareness: Asynchronous code should avoid blocking threads, especially during I/O operations.
- Contract consistency: An implementation must honor the behavior promised by its interface or base abstraction.
II. Language Integrated Query (LINQ)
A. LINQ
LINQ is a C# query technology that applies a consistent syntax to collections, databases, XML, and other data sources.
- Query expression: A query can filter and project objects using familiar clauses.
var names = from student in students
where student.Mark >= 50
select student.Name;Here, students is the source sequence, Mark >= 50 is the filter, and Name is the projected result.
- Method syntax: The same operation can use extension methods and lambda expressions.
var names = students
.Where(s => s.Mark >= 50)
.Select(s => s.Name);- Deferred execution:
WhereandSelectusually store the query definition; iteration withforeach,ToList(), orCount()triggers execution. - Immediate execution:
ToList(),ToArray(), andToDictionary()materialize results immediately, which is useful when the source may change. - Composition: Queries can be built step by step, for example filtering with
Where, ordering withOrderBy, grouping withGroupBy, and aggregating withSumorAverage. - Provider distinction:
IEnumerable<T>queries normally run in memory, whereasIQueryable<T>can translate expressions into another language, such as SQL.
B. LINQ to Collections
LINQ to Collections queries in-memory objects implementing IEnumerable<T>, including arrays, lists, and sets.
- Filtering:
Wherereturns elements satisfying a predicate.
var adults = people.Where(p => p.Age >= 18).ToList();The predicate p => p.Age >= 18 is evaluated for each Person.
- Projection:
Selecttransforms each source element, such as convertingPersonobjects into names. - Ordering:
OrderBy(p => p.Name)sorts ascending;OrderByDescending(p => p.Mark)sorts descending. - Element selection:
FirstOrDefault()returns the first match or the default value, whileSingle()requires exactly one match and throws if zero or multiple matches exist. - Aggregation:
Count(),Min(),Max(),Sum(), andAverage()calculate summary values;Any()efficiently checks whether at least one element exists. - Performance limitation: Repeated enumeration can repeat work. Materializing with
ToList()is appropriate when a result will be reused, but it allocates additional memory.
III. Delegates and Events
Delegates and events provide type-safe mechanisms for treating methods as values and notifying objects when something occurs.
A. Delegates and Events
A delegate is a type-safe reference to a method; an event restricts delegate invocation so that only its declaring class can raise the notification.
- Delegate declaration: The signature specifies the return type and parameters.
public delegate void MessageHandler(string message);MessageHandler can reference any compatible method returning void and accepting one string.
- Invocation: A delegate may point to a method or lambda.
MessageHandler handler = message => Console.WriteLine(message);
handler("Saved");- Built-in delegates:
Action<T>returnsvoid,Func<T, TResult>returns a value, andPredicate<T>returnsbool. - Multicast behavior: Delegates can contain multiple methods using
+=; invocation calls them in registration order. - Event contract: An event commonly uses
EventHandlerorEventHandler<TEventArgs>.
public event EventHandler? Completed;
protected virtual void OnCompleted() =>
Completed?.Invoke(this, EventArgs.Empty);- Encapsulation: External code can subscribe with
+=and unsubscribe with-=, but cannot directly raise the event. - Lifetime concern: Long-lived publishers retaining subscribers can cause memory retention; unsubscribe when the subscriber no longer needs notifications.
IV. Interfaces
Interfaces define contracts that classes or structs implement, enabling polymorphism and dependency substitution.
A. Interfaces
An interface states what an object can do without prescribing how it does it.
- Contract:
IPrintablemight declarevoid Print(), requiring every implementation to provide that operation. - Multiple implementation: A class can implement multiple interfaces, such as
class Report : IPrintable, IExportable. - Polymorphism: Code can depend on
IEnumerable<string>rather thanList<string>, allowing arrays, lists, or custom sequences. - Explicit implementation: A class can provide different interface members with the same name, but the member is accessed through the interface reference.
- Default members: Modern C# interfaces may contain default implementations, but interfaces should remain focused contracts rather than large utility classes.
- Testing benefit: A service accepting
IEmailSendercan receive a fake sender during testing instead of contacting a real mail server.
V. Generics in C
Generics define classes, methods, and interfaces with type parameters, providing reuse while preserving compile-time type safety.
A. Generics in C
Generics allow one implementation to work with many types without unsafe casts or unnecessary boxing.
- Generic type:
List<int>stores integers, whileList<string>stores strings; the compiler rejects an incorrect insertion. - Generic method:
static T First<T>(IEnumerable<T> items) => items.First();T is a type parameter inferred from the supplied sequence.
- Constraints: Constraints limit valid type arguments.
static T Create<T>() where T : new() => new T();The new() constraint requires a public parameterless constructor.
- Common constraints:
where T : classrequires a reference type;structrequires a non-nullable value type; an interface constraint requires implementation of that interface. - Value-type efficiency:
List<int>stores integers without converting each value toobject, avoiding many boxing operations. - Generic abstractions:
IRepository<T>can describe storage operations forCustomer,Order, or another entity while retaining the entity type. - Variance:
IEnumerable<Derived>can be used whereIEnumerable<Base>is expected becauseIEnumerable<out T>is covariant.
VI. Asynchronous Programming using Async and Await
Asynchronous programming allows an operation to pause without blocking the current thread while it waits for I/O or another asynchronous result.
A. Asynchronous Programming using Async and Await
The async and await keywords express continuation-based code in a readable form.
- Task result:
Taskrepresents an operation with no returned value;Task<T>represents an operation producing aT.
public async Task<string> LoadAsync(HttpClient client, string url)
{
return await client.GetStringAsync(url);
}The method returns a Task<string> and resumes after the HTTP operation completes.
- Non-blocking wait:
awaitdoes not normally block the calling thread;.Resultand.Wait()can block and may cause deadlocks in context-sensitive applications. - Concurrency: Independent operations can be started before awaiting them.
Task<string> first = LoadAsync(client, url1);
Task<string> second = LoadAsync(client, url2);
string[] pages = await Task.WhenAll(first, second);- Exceptions: Exceptions from an awaited task are observed at the
awaitexpression and can be handled withtry/catch. - Cancellation: Pass a
CancellationTokento operations and callThrowIfCancellationRequested()where appropriate. - CPU versus I/O: Async is especially useful for file, database, and network I/O. CPU-heavy work may require
Task.Run, used carefully because it consumes thread-pool resources. - Naming convention: Asynchronous methods conventionally end in
Async, such asSaveAsync, and should propagate asynchronous calls instead of synchronously wrapping them.
VII. Single Responsibility Principle (SRP)
SRP states that a class should have one responsibility and therefore one reason to change.
A. Single Responsibility Principle (SRP)
SRP separates unrelated causes of modification so that changes remain localized.
- Mixed responsibility: A
Reportclass that formats content, writes files, and emails users changes for presentation, storage, and delivery reasons. - Separation: Use
ReportFormatter,ReportFileWriter, andReportEmailSender, each focused on one concern. - Cohesion: Members of an SRP-compliant class should support one closely related purpose, such as validating an order.
- Practical limit: SRP does not mean every class has one method; it means the class has one coherent responsibility.
VIII. Open/Closed Principle (OCP)
OCP states that software entities should be open for extension but closed for modification.
A. Open/Closed Principle (OCP)
A stable abstraction should allow new behavior through new implementations rather than repeated edits to existing decision logic.
- Violation: A shipping method contains
ifbranches for"Air","Sea", and every future method. - Extension: Define
IShippingCalculator.Calculate(Order order)and addAirShippingCalculatororSeaShippingCalculator. - Polymorphic selection: A service can call the interface without knowing the concrete calculation.
- Trade-off: Abstractions should be introduced around likely variation; unnecessary interfaces increase complexity without improving extensibility.
IX. Liskov Substitution Principle (LSP)
LSP requires that objects of a derived type can replace objects of their base type without breaking expected program behavior.
A. Liskov Substitution Principle (LSP)
Subtypes must honor the base contract, including valid inputs, outputs, and behavioral guarantees.
- Contract example: If
ReadOnlyFilederives fromFilebut throwsNotSupportedExceptionfromWrite, callers expecting everyFileto be writable are misled. - Behavioral consistency: A subtype should not strengthen preconditions, such as accepting fewer valid inputs than its base type.
- Design response: Separate writable and readable abstractions, for example
IReadableandIWritable, when capabilities differ. - Result: Correct substitution reduces type checks and prevents runtime surprises.
X. Interface Segregation Principle (ISP)
ISP states that clients should not be forced to depend on methods they do not use.
A. Interface Segregation Principle (ISP)
Small, role-specific interfaces make implementations and consumers more focused.
- Large interface:
IMachinecontainingPrint,Scan,Fax, andStapleburdens a simple printer with unsupported members. - Segregation: Define
IPrinter.Print()andIScanner.Scan()separately. - Client dependency: A printing service depends only on
IPrinter, so scanner changes do not affect it. - Design caution: Interfaces should be split by client needs and meaningful capabilities, not arbitrarily into single-method fragments.
XI. Dependency Inversion Principle (DIP)
DIP states that high-level modules should not depend directly on low-level modules; both should depend on abstractions.
A. Dependency Inversion Principle (DIP)
Business logic should express its required services through interfaces, while concrete infrastructure is supplied from outside.
- Direct dependency:
OrderServiceconstructingSqlOrderRepository()tightly couples business logic to SQL storage. - Abstraction: Depend on
IOrderRepositoryand inject the implementation.
public OrderService(IOrderRepository repository)
{
_repository = repository;
}- Dependency injection: A composition root, often application startup, maps
IOrderRepositorytoSqlOrderRepository. - Testability: Tests can inject
FakeOrderRepository, isolating order logic from a database. - Stable direction: High-level policy owns or references the abstraction; low-level details implement it.
- Practical boundary: Use DIP at genuine change or infrastructure boundaries, such as databases, files, clocks, and external APIs.
Did this save you a night before the exam?
LPU Notes is free, and it stays free. Ads cover part of the server bill. The rest comes out of a student's own pocket: the domain, the storage, and keeping the site up through the weeks everyone needs it at once.
The payment button didn't load. An ad blocker or a filtered network is the usual reason. to try again.
Nothing here is ever locked, and nothing unlocks. Chip in only if it was worth it. What it pays for →