2.1 - Syntax and Structure
C# is a modern, strongly-typed, object-oriented programming language developed by Microsoft as part of the .NET platform. It combines the power and flexibility of C++ with the simplicity and productivity of languages like Java, while adding unique features of its own. This chapter explores the fundamental syntax and structure of C# programs.
🔰 Beginner's Corner: Understanding Programming Syntax​
If you're new to programming, think of syntax as the grammar rules of a programming language. Just as English has rules about how to structure sentences with subjects, verbs, and punctuation, C# has specific rules about how to write code.
What is syntax?
- The set of rules that defines how to write code that the computer can understand
- Includes rules about punctuation, word order, and how to structure statements
- Determines what is valid code and what will cause errors
Why is syntax important?
- Computers are precise and cannot guess what you mean
- Even small syntax errors will prevent your program from running
- Learning the correct syntax is like learning to spell and punctuate in a new language
Key syntax elements in C# you'll learn about:
- Statements and how they end with semicolons (
;
) - How to group code using curly braces (
{
and}
) - How to write comments that the computer ignores
- How to name things (variables, methods, classes) following C# conventions
Don't worry if it seems overwhelming at first. With practice, writing with correct syntax will become second nature!
2.1.1 - C# Language Characteristics​
Before diving into the syntax details, let's understand the key characteristics that define C#:
2.1.1.1 - Core Language Features​
- Strongly Typed: Variables must be declared with a specific type, and type safety is enforced at compile time.
- Object-Oriented: Supports encapsulation, inheritance, and polymorphism through classes and interfaces.
- Component-Oriented: Enables building reusable components through properties, events, and attributes.
- Type-Safe: Prevents unsafe casts and unauthorized memory access, reducing common programming errors.
- Garbage Collected: Automatic memory management frees developers from manual memory allocation and deallocation.
- Modern Syntax: Offers concise syntax with features like lambda expressions, pattern matching, and null-conditional operators.
- Interoperable: Can interact with code written in other languages and platforms through various mechanisms.
2.1.1.2 - Evolution of C#​
C# continues to evolve with regular updates that introduce new features and improvements:
Version | Year | Key Features |
---|---|---|
C# 1.0 | 2002 | Basic language features, classes, structs, interfaces |
C# 2.0 | 2005 | Generics, partial types, anonymous methods, nullable types |
C# 3.0 | 2007 | LINQ, lambda expressions, extension methods, anonymous types |
C# 4.0 | 2010 | Dynamic binding, named/optional parameters, improved COM interop |
C# 5.0 | 2012 | Async/await pattern for asynchronous programming |
C# 6.0 | 2015 | String interpolation, null-conditional operators, expression-bodied members |
C# 7.0-7.3 | 2017-2018 | Tuples, pattern matching, local functions, ref returns |
C# 8.0 | 2019 | Nullable reference types, async streams, default interface methods |
C# 9.0 | 2020 | Records, init-only properties, top-level statements, pattern matching enhancements |
C# 10.0 | 2021 | Global using directives, file-scoped namespaces, record structs |
C# 11.0 | 2022 | Raw string literals, required members, generic attributes |
C# 12.0 | 2023 | Primary constructors, collection expressions, inline arrays |
2.1.2 - Basic Structure of a C# Program​
A C# program follows a structured approach with specific elements that define its organization and execution flow.
2.1.2.1 - Traditional Program Structure​
In its traditional form, a C# program consists of:
- Using Directives: Import namespaces to access types without fully qualifying them
- Namespace Declaration: Organize code and prevent naming conflicts
- Type Declarations: Define classes, structs, interfaces, enums, or records
- Main Method: The entry point where program execution begins
Here's a simple "Hello, World!" example that demonstrates this structure:
// Using directive to import the System namespace
using System;
// Namespace declaration
namespace HelloWorld
{
// Class declaration
public class Program
{
/// <summary>
/// The entry point of the application.
/// </summary>
/// <param name="args">Command-line arguments passed to the program.</param>
public static void Main(string[] args)
{
// Application code
Console.WriteLine("Hello, World!");
}
}
}
2.1.2.2 - Modern C# Program Structure (C# 9.0+)​
Starting with C# 9.0, you can use top-level statements to create more concise programs without explicitly defining a class or Main method:
// Using directive
using System;
// Top-level statements - code written directly in the file
Console.WriteLine("Hello, World!");
// Variables and other statements can be included
string name = "C# Developer";
Console.WriteLine($"Hello, {name}!");
// Behind the scenes, the compiler still generates a Program class and Main method
When using top-level statements:
- Only one file in your application can use top-level statements
- All top-level statements must precede any type or namespace declarations
- The compiler automatically generates a Program class with a Main method containing your statements
2.1.2.3 - Anatomy of a C# Program​
Let's break down the key elements of a C# program:
Using Directives​
using System; // Import the System namespace
using System.Collections.Generic; // Import for collections like List<T>
using System.Linq; // Import for LINQ query operations
using static System.Math; // Import static members (C# 6.0+)
using Console = System.Console; // Create an alias for a type
Namespace Declaration​
// Traditional namespace declaration
namespace MyApplication
{
// Types and members go here
}
// File-scoped namespace declaration (C# 10.0+)
namespace MyApplication;
// Types and members go here
Class Declaration​
// Public class accessible from other assemblies
public class Program
{
// Class members (fields, properties, methods, etc.)
}
// Internal class accessible only within the same assembly
internal class Helper
{
// Class members
}
Main Method​
// Void Main method that doesn't return a value
public static void Main(string[] args)
{
// Method body
}
// Main method that returns an integer exit code
public static int Main(string[] args)
{
// Method body
return 0; // Return exit code (0 typically indicates success)
}
// Main method without command-line arguments parameter
public static void Main()
{
// Method body
}
2.1.3 - C# Syntax Fundamentals​
Understanding the basic syntax rules of C# is essential for writing correct and maintainable code.
2.1.3.1 - Statements and Expressions​
- Statements: Complete units of execution that perform actions
- Expressions: Combinations of values, variables, and operators that evaluate to a value
// Statement (declaration)
int x;
// Statement (assignment)
x = 10;
// Statement with expression (declaration and initialization)
int y = x + 5;
// Statement (method call)
Console.WriteLine(y);
// Expression (used within a statement)
x + y * 2
2.1.3.2 - Statement Termination​
In C#, statements are terminated with a semicolon (;
):
int a = 5; // Statement 1
int b = 10; // Statement 2
int sum = a + b; // Statement 3
Console.WriteLine(sum); // Statement 4
2.1.3.3 - Code Blocks​
Code blocks are enclosed in curly braces {}
and group multiple statements together:
if (x > 0)
{
// This is a code block
Console.WriteLine("x is positive");
x = x * 2;
}
2.1.3.4 - Case Sensitivity​
C# is case-sensitive, meaning that identifiers with different casing are considered distinct:
int Value = 10; // Different from 'value'
int value = 20; // Different from 'Value'
// These refer to different variables
Console.WriteLine(Value); // Outputs: 10
Console.WriteLine(value); // Outputs: 20
2.1.3.5 - Identifiers​
Identifiers are names used for variables, methods, classes, and other program elements:
- Must start with a letter or underscore
- Can contain letters, digits, and underscores
- Cannot be a C# keyword (unless prefixed with
@
) - Are case-sensitive
// Valid identifiers
int count;
string firstName;
double _value;
bool isValid;
// Using a keyword as an identifier with @ prefix
string @class = "Computer Science";
// Invalid identifiers
// int 1count; // Cannot start with a digit
// string first-name; // Cannot contain hyphens
// double void; // Cannot use keywords without @
2.1.3.6 - Naming Conventions​
C# follows specific naming conventions for different program elements:
Element | Convention | Example |
---|---|---|
Namespace | PascalCase | System.Collections |
Class/Struct | PascalCase | StringBuilder |
Interface | PascalCase with 'I' prefix | IDisposable |
Method | PascalCase | CalculateTotal |
Property | PascalCase | FirstName |
Field (private) | camelCase with underscore prefix | _counter |
Field (public) | PascalCase | MaxValue |
Parameter | camelCase | firstName |
Local variable | camelCase | totalAmount |
Constant | PascalCase or ALL_CAPS | MaximumValue or MAX_VALUE |
namespace MyApplication
{
public interface IDataProcessor
{
void ProcessData(string inputData);
}
public class DataManager : IDataProcessor
{
private readonly int _maxRetries = 3;
public const int MaxBufferSize = 1024;
private string _connectionString;
public string ConnectionString
{
get { return _connectionString; }
set { _connectionString = value; }
}
public void ProcessData(string inputData)
{
int retryCount = 0;
bool isProcessed = false;
// Method implementation
}
}
}
2.1.2 - Writing Comments​
In C#, comments are crucial for improving the readability of the code, documenting its functionality, and explaining complex logic that isn't immediately obvious from the code itself. Effective use of comments can greatly assist other developers in understanding the intentions behind the code, which facilitates maintenance and future modifications. Here's an overview of how to write comments in C#:
2.1.2.1 - Types of Comments in C#​
C# supports three main types of comments:
-
Single-line Comments
- Use two forward slashes
//
to start a single-line comment. Anything following//
on the same line will be treated as a comment and ignored by the compiler.
Example:
// This is a single-line comment
int x = 5; // This initializes x with 5 - Use two forward slashes
-
Multi-line Comments
- Use
/*
to begin a multi-line comment and*/
to end it. Everything between/*
and*/
will be commented out. This type of comment can span multiple lines and is useful for longer descriptions or temporarily disabling blocks of code.
Example:
/* This is a multi-line comment
and it continues across multiple lines
until it is closed. */
int y = 10; - Use
-
XML Documentation Comments
- Use three forward slashes
///
to write XML documentation comments. These comments are special because they can be processed to generate external documentation for your code, and they can also provide contextual information in Visual Studio through IntelliSense.
Example:
/// <summary>
/// Calculates the square of a number.
/// </summary>
/// <param name="x">The number to square.</param>
/// <returns>The square of the number.</returns>
public int Square(int x)
{
return x * x;
} - Use three forward slashes
2.1.2.2 - Best Practices for Writing Comments​
- Clarity Over Quantity: Write clear and concise comments that explain the "why" behind the code, not just the "what". Avoid stating the obvious; focus on complex logic or important decisions made during the coding process.
- Maintain Comments: Ensure that comments are updated alongside the code changes. Outdated comments can be misleading and more harmful than no comments at all.
- Use Comments for Code Documentation: Utilize XML comments to document the purpose, parameters, and return values of functions and methods. This practice helps maintain comprehensive documentation and assists other developers and tools in understanding your code.
- Avoid Commented-Out Code: It's often tempting to leave old code commented out for future reference. However, this can clutter the codebase. Prefer version control systems to keep track of previous code versions instead of commenting out unused code.
2.1.2.3 - Example of Good Commenting Practice​
Here’s a simple example to illustrate good commenting practices in a C# method:
/// <summary>
/// Calculate the average of two numbers.
/// </summary>
/// <param name="num1">The first number.</param>
/// <param name="num2">The second number.</param>
/// <returns>The average value.</returns>
public double CalculateAverage(double num1, double num2)
{
// Sum the numbers
double sum = num1 + num2;
// Divide by the number of elements (2) to get the average
double average = sum / 2;
return average;
}
This example combines XML documentation comments with inline comments to clearly document the method's functionality and to explain the steps involved in the calculation, thereby enhancing readability and maintainability.
2.1.3 - Expanded Hello World Program​
using System;
namespace HelloWorld
{
public class Program
{
// Main method is the entry point of any C# application.
// 'args' are the command-line arguments passed when the program is run.
public static void Main(string[] args)
{
// Get the current time to personalize the greeting
DateTime currentTime = DateTime.Now;
string timeOfDay = currentTime.Hour < 12 ? "morning" :
(currentTime.Hour < 18 ? "afternoon" : "evening");
// Check if any command-line arguments were provided.
if (args.Length > 0)
{
// If an argument is provided, use it in the greeting with string interpolation.
Console.WriteLine($"Good {timeOfDay}, {args[0]}!");
}
else
{
// If no arguments were provided, default to a generic greeting with string interpolation.
Console.WriteLine($"Good {timeOfDay}, World!");
}
// Display the current date and time using string interpolation
Console.WriteLine($"Today is {currentTime:dddd, MMMM d, yyyy} at {currentTime:h:mm tt}");
// Instructs the user on how to close the application using string interpolation.
Console.WriteLine($"Press any key to exit");
Console.Write("..."); // Prints to the console without adding a new line.
// Waits for a key press to close the console window.
// This prevents the console from closing immediately after displaying the message
// when running the executable directly.
ConsoleKeyInfo keyInfo = Console.ReadKey();
// Show which key was pressed (demonstrating another use of string interpolation)
Console.WriteLine($"\nYou pressed: {keyInfo.KeyChar}");
}
}
}
2.1.4 - Sample Getting Input Program​
using System;
namespace GettingInput
{
public class Program
{
public static void Main()
{
// Prompt the user for their age with clear instructions
Console.WriteLine("Please enter your age (a number between 1 and 123):");
// Store the user's input
string input = Console.ReadLine();
// Validate the input
if (string.IsNullOrWhiteSpace(input))
{
Console.WriteLine("You didn't enter anything. Please try again.");
return;
}
// Attempt to parse the input into an integer
bool isValidNumber = int.TryParse(input, out int age);
// Check if the input is a valid number
if (!isValidNumber)
{
Console.WriteLine($"'{input}' is not a valid number. Please enter digits only.");
return;
}
// Check if the age is within a reasonable human age range
if (age <= 0)
{
Console.WriteLine("Age must be a positive number.");
return;
}
else if (age > 123) // 123 is the age of the oldest verified living person
{
Console.WriteLine("The age you entered exceeds the oldest known human age. Please enter a realistic age.");
return;
}
// If the input is valid, provide a personalized response based on age category
string ageCategory = DetermineAgeCategory(age);
Console.WriteLine($"You are {age} years old! That makes you a {ageCategory}.");
// Additional information based on age
if (age < 18)
{
Console.WriteLine("You are considered a minor in most countries.");
}
else if (age >= 65)
{
Console.WriteLine("You may be eligible for senior benefits in many places.");
}
}
/// <summary>
/// Determines the age category based on the given age.
/// </summary>
/// <param name="age">The age to categorize.</param>
/// <returns>A string representing the age category.</returns>
private static string DetermineAgeCategory(int age)
{
return age switch
{
< 13 => "child",
< 20 => "teenager",
< 30 => "young adult",
< 60 => "adult",
< 80 => "senior",
_ => "elder"
};
}
}
}
2.1.5 - C# Code Organization​
Proper code organization is essential for creating maintainable and scalable applications. C# provides several mechanisms to structure your code effectively.
2.1.5.1 - Files and Projects​
A typical C# solution consists of multiple projects, each containing multiple files:
- Solution (.sln): A container for one or more related projects
- Project (.csproj): A collection of files that compile into an assembly (.dll or .exe)
- Source Files (.cs): Individual C# code files
MySolution/
├── MyApp.sln
├── MyApp.Core/
│ ├── MyApp.Core.csproj
│ ├── Models/
│ │ ├── User.cs
│ │ └── Product.cs
│ └── Services/
│ ├── UserService.cs
│ └── ProductService.cs
├── MyApp.UI/
│ ├── MyApp.UI.csproj
│ ├── Program.cs
│ └── Forms/
│ ├── MainForm.cs
│ └── LoginForm.cs
└── MyApp.Tests/
├── MyApp.Tests.csproj
├── UserServiceTests.cs
└── ProductServiceTests.cs
2.1.5.2 - Namespaces​
Namespaces organize code and prevent naming conflicts. They create a hierarchical structure that reflects the logical organization of your application:
// File: User.cs
namespace MyApp.Core.Models
{
public class User
{
// Implementation
}
}
// File: UserService.cs
namespace MyApp.Core.Services
{
using MyApp.Core.Models;
public class UserService
{
public User GetUser(int id)
{
// Implementation
}
}
}
2.1.5.3 - Access Modifiers​
Access modifiers control the visibility and accessibility of types and members:
Modifier | Description |
---|---|
public | Accessible from any code |
private | Accessible only within the containing type |
protected | Accessible within the containing type and derived types |
internal | Accessible within the same assembly |
protected internal | Accessible within the same assembly or derived types |
private protected | Accessible within the same assembly by derived types |
public class Customer
{
// Public property - accessible from anywhere
public string Name { get; set; }
// Private field - accessible only within this class
private decimal _creditLimit;
// Protected method - accessible within this class and derived classes
protected bool CheckCreditLimit(decimal amount)
{
return amount <= _creditLimit;
}
// Internal method - accessible within the same assembly
internal void UpdateCreditHistory(decimal amount)
{
// Implementation
}
}
2.1.5.4 - Partial Classes and Methods​
Partial classes allow splitting a class, struct, or interface definition across multiple files:
// File: Customer.cs
public partial class Customer
{
public string Name { get; set; }
public string Email { get; set; }
public partial void Validate();
}
// File: Customer.Validation.cs
public partial class Customer
{
public partial void Validate()
{
// Validation logic
}
}
2.1.5.5 - Nested Types​
Types can be defined within other types to indicate a close relationship:
public class Order
{
// Nested enum
public enum Status
{
Pending,
Processing,
Shipped,
Delivered,
Cancelled
}
// Nested class
public class LineItem
{
public int ProductId { get; set; }
public int Quantity { get; set; }
public decimal Price { get; set; }
}
public int OrderId { get; set; }
public Status OrderStatus { get; set; }
public List<LineItem> Items { get; set; }
}
// Usage
Order order = new Order();
order.OrderStatus = Order.Status.Processing;
order.Items.Add(new Order.LineItem { ProductId = 1, Quantity = 2, Price = 10.99m });
2.1.6 - C# Coding Conventions​
Following consistent coding conventions improves code readability and maintainability. Here are the recommended conventions for C# code:
2.1.6.1 - Layout Conventions​
Indentation and Spacing​
- Use 4 spaces for indentation (not tabs)
- Add a space after keywords like
if
,for
,while
- Add a space around operators (
+
,-
,=
, etc.) - Don't add a space after method names in method calls
// Good
if (condition)
{
DoSomething();
}
// Good
for (int i = 0; i < 10; i++)
{
Console.WriteLine(i);
}
// Good
int sum = a + b;
// Good
CalculateTotal(price, tax);
Braces​
- Place opening braces on a new line
- Align closing braces with the opening statement
- Always use braces for control statements, even for single-line bodies
// Good
if (condition)
{
DoSomething();
}
// Avoid (no braces for single statement)
if (condition)
DoSomething();
Line Length and Wrapping​
- Keep lines to a reasonable length (80-120 characters)
- When wrapping method parameters, align them with the first parameter
// Good
public void SomeMethodWithManyParameters(
int parameter1,
string parameter2,
bool parameter3,
double parameter4)
{
// Method body
}
2.1.6.2 - Naming Conventions​
- Use meaningful, descriptive names
- Avoid abbreviations and acronyms unless they're widely known
- Follow the standard naming conventions for different elements (as described earlier)
// Good
public class CustomerRepository
{
private readonly DatabaseContext _context;
public Customer GetCustomerById(int customerId)
{
// Implementation
}
}
// Avoid
public class CustRepo
{
private readonly DbCtx _ctx;
public Customer GetCustById(int cId)
{
// Implementation
}
}
2.1.6.3 - Code Organization​
- Organize members in a consistent order (fields, properties, constructors, methods)
- Group related members together
- Place private members after public ones
public class Customer
{
// Fields
private readonly ILogger _logger;
private string _notes;
// Properties
public int Id { get; set; }
public string Name { get; set; }
// Constructors
public Customer(ILogger logger)
{
_logger = logger;
}
// Public methods
public void PlaceOrder(Order order)
{
ValidateOrder(order);
SaveOrder(order);
}
// Private methods
private void ValidateOrder(Order order)
{
// Implementation
}
private void SaveOrder(Order order)
{
// Implementation
}
}
2.1.6.4 - Language Guidelines​
- Prefer
var
when the type is obvious from the right side of the assignment - Use string interpolation (
$"..."
) instead ofstring.Format()
- Use object initializers when appropriate
- Use expression-bodied members for simple methods and properties
// Use var when type is obvious
var customers = new List<Customer>();
// Use string interpolation
string greeting = $"Hello, {name}!";
// Use object initializers
var customer = new Customer
{
Id = 1,
Name = "John Doe",
Email = "john@example.com"
};
// Use expression-bodied members
public string FullName => $"{FirstName} {LastName}";
public double GetArea() => Width * Height;
2.1.7 - Practical Examples​
Let's examine some practical examples that demonstrate C# syntax and structure in real-world scenarios.
2.1.7.1 - Console Application with Command-Line Arguments​
using System;
namespace FileProcessor
{
/// <summary>
/// A simple file processing application that demonstrates command-line argument handling.
/// </summary>
public class Program
{
/// <summary>
/// The entry point of the application.
/// </summary>
/// <param name="args">Command-line arguments: [input-file] [output-file]</param>
public static void Main(string[] args)
{
// Display application banner
Console.WriteLine("File Processor v1.0");
Console.WriteLine("-------------------");
// Validate command-line arguments
if (args.Length < 2)
{
DisplayUsage();
return;
}
string inputFile = args[0];
string outputFile = args[1];
try
{
// Process the file
ProcessFile(inputFile, outputFile);
Console.WriteLine($"Successfully processed {inputFile} to {outputFile}");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
return;
}
}
/// <summary>
/// Displays usage information for the application.
/// </summary>
private static void DisplayUsage()
{
Console.WriteLine("Usage: FileProcessor <input-file> <output-file>");
Console.WriteLine("Example: FileProcessor input.txt output.txt");
}
/// <summary>
/// Processes the input file and writes the results to the output file.
/// </summary>
/// <param name="inputFile">Path to the input file.</param>
/// <param name="outputFile">Path to the output file.</param>
private static void ProcessFile(string inputFile, string outputFile)
{
// Implementation would go here
// For example:
// string content = File.ReadAllText(inputFile);
// string processed = content.ToUpper();
// File.WriteAllText(outputFile, processed);
}
}
}
2.1.7.2 - Class Library with Interfaces and Inheritance​
using System;
using System.Collections.Generic;
namespace ShapeLibrary
{
/// <summary>
/// Represents a geometric shape with methods to calculate area and perimeter.
/// </summary>
public interface IShape
{
/// <summary>
/// Calculates the area of the shape.
/// </summary>
/// <returns>The area of the shape.</returns>
double CalculateArea();
/// <summary>
/// Calculates the perimeter of the shape.
/// </summary>
/// <returns>The perimeter of the shape.</returns>
double CalculatePerimeter();
}
/// <summary>
/// Base class for all shapes that provides common functionality.
/// </summary>
public abstract class Shape : IShape
{
/// <summary>
/// Gets or sets the name of the shape.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Initializes a new instance of the Shape class with the specified name.
/// </summary>
/// <param name="name">The name of the shape.</param>
protected Shape(string name)
{
Name = name;
}
/// <summary>
/// Calculates the area of the shape.
/// </summary>
/// <returns>The area of the shape.</returns>
public abstract double CalculateArea();
/// <summary>
/// Calculates the perimeter of the shape.
/// </summary>
/// <returns>The perimeter of the shape.</returns>
public abstract double CalculatePerimeter();
/// <summary>
/// Returns a string representation of the shape.
/// </summary>
/// <returns>A string representation of the shape.</returns>
public override string ToString()
{
return $"{Name}: Area = {CalculateArea():F2}, Perimeter = {CalculatePerimeter():F2}";
}
}
/// <summary>
/// Represents a circle shape.
/// </summary>
public class Circle : Shape
{
/// <summary>
/// Gets or sets the radius of the circle.
/// </summary>
public double Radius { get; set; }
/// <summary>
/// Initializes a new instance of the Circle class with the specified radius.
/// </summary>
/// <param name="radius">The radius of the circle.</param>
/// <exception cref="ArgumentException">Thrown when radius is negative or zero.</exception>
public Circle(double radius) : base("Circle")
{
if (radius <= 0)
{
throw new ArgumentException("Radius must be positive", nameof(radius));
}
Radius = radius;
}
/// <summary>
/// Calculates the area of the circle.
/// </summary>
/// <returns>The area of the circle.</returns>
public override double CalculateArea()
{
return Math.PI * Radius * Radius;
}
/// <summary>
/// Calculates the perimeter (circumference) of the circle.
/// </summary>
/// <returns>The perimeter of the circle.</returns>
public override double CalculatePerimeter()
{
return 2 * Math.PI * Radius;
}
}
/// <summary>
/// Represents a rectangle shape.
/// </summary>
public class Rectangle : Shape
{
/// <summary>
/// Gets or sets the width of the rectangle.
/// </summary>
public double Width { get; set; }
/// <summary>
/// Gets or sets the height of the rectangle.
/// </summary>
public double Height { get; set; }
/// <summary>
/// Initializes a new instance of the Rectangle class with the specified width and height.
/// </summary>
/// <param name="width">The width of the rectangle.</param>
/// <param name="height">The height of the rectangle.</param>
/// <exception cref="ArgumentException">Thrown when width or height is negative or zero.</exception>
public Rectangle(double width, double height) : base("Rectangle")
{
if (width <= 0 || height <= 0)
{
throw new ArgumentException("Width and height must be positive");
}
Width = width;
Height = height;
}
/// <summary>
/// Calculates the area of the rectangle.
/// </summary>
/// <returns>The area of the rectangle.</returns>
public override double CalculateArea()
{
return Width * Height;
}
/// <summary>
/// Calculates the perimeter of the rectangle.
/// </summary>
/// <returns>The perimeter of the rectangle.</returns>
public override double CalculatePerimeter()
{
return 2 * (Width + Height);
}
}
/// <summary>
/// Utility class for working with collections of shapes.
/// </summary>
public static class ShapeUtility
{
/// <summary>
/// Calculates the total area of all shapes in the collection.
/// </summary>
/// <param name="shapes">The collection of shapes.</param>
/// <returns>The total area of all shapes.</returns>
public static double CalculateTotalArea(IEnumerable<IShape> shapes)
{
double totalArea = 0;
foreach (var shape in shapes)
{
totalArea += shape.CalculateArea();
}
return totalArea;
}
/// <summary>
/// Finds the shape with the largest area in the collection.
/// </summary>
/// <param name="shapes">The collection of shapes.</param>
/// <returns>The shape with the largest area, or null if the collection is empty.</returns>
public static IShape FindLargestShape(IEnumerable<IShape> shapes)
{
IShape largestShape = null;
double largestArea = 0;
foreach (var shape in shapes)
{
double area = shape.CalculateArea();
if (largestShape == null || area > largestArea)
{
largestShape = shape;
largestArea = area;
}
}
return largestShape;
}
}
}
2.1.8 - Summary​
In this chapter, we've explored the fundamental syntax and structure of C# programs, including:
- The basic structure of a C# program with namespaces, classes, and methods
- Modern C# features like top-level statements
- C# syntax fundamentals including statements, expressions, and code blocks
- Naming conventions and identifier rules
- Writing effective comments and documentation
- Code organization techniques with namespaces, access modifiers, and partial classes
- C# coding conventions for layout, naming, and language features
- Practical examples demonstrating these concepts in real-world scenarios
Understanding these foundational elements is crucial for writing clean, maintainable C# code. As you progress in your C# journey, these principles will serve as the building blocks for more advanced concepts and applications.