Table of Contents
- C# Language Basics 1.1 Syntax Overview 1.2 Variables and Data Types 1.3 Operators and Expressions 1.4 Control Flow Statements 1.5 Methods and Parameters
C# Language Basics
Syntax Overview
If you're coming from a Java background, you'll find C# surprisingly familiar yet distinctive. Both languages share a C-style syntax, but C# introduces several unique constructts worth exploring.
Program Structure
A basic C# application follows this pattern:
using System;
namespace MyApplication
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Greetings, World!");
}
}
}
Comparing to the equivalent Java implementation:
public class MyApplication {
public static void main(String[] args) {
System.out.println("Greetings, World!");
}
}
Key differences emerge in several areas. C# employs using directives for namespace imports rather then Java's import statement. The entry point signature differs slightly: C# uses static void Main(string[] args) while Java requires public static void main(String[] args). Output operations also vary, with C# favoring Console.WriteLine() against Java's System.out.println().
Modern C# (version 9 and later) supports top-level statements, eliminating the need for explicit namespaces and class declarations:
Console.WriteLine("Greetings, World!");
Naming Conventions
C# and Java follow different naming conventions:
| Element | C# Convention | Java Convention |
|---|---|---|
| Methods, Properties | PascalCase | camelCase |
| Local Variables, Parameters | camelCase | camelCase |
| Interfaces | Prefix with "I" | No special prefix |
| Constants | PascalCase | UPPER_SNAKE_CASE |
Primitive Data Types
Both languages share common numeric types, though C# provides additional unsigned variants:
// C# primitives
sbyte signedByte = -128; // 8-bit signed
byte unsignedByte = 255; // 8-bit unsigned
short shortVal = 32000; // 16-bit
ushort uShortVal = 65000; // 16-bit unsigned
int count = 42; // 32-bit signed
uint positiveCount = 100u; // 32-bit unsigned
long largeNumber = 999999L; // 64-bit signed
ulong bigPositive = 999999UL; // 64-bit unsigned
float pi = 3.14f; // 32-bit floating point
double precision = 3.14159; // 64-bit floating point
decimal currency = 99.99m; // High-precision decimal
bool flag = true; // Boolean
char letter = 'A'; // 16-bit character
string text = "Hello"; // Reference type string
Arrays
Array declaration syntax differs slightly between the two languages:
// C# array syntax
int[] scores = new int[5];
string[] names = { "Emma", "James", "Sophia" };
// Java array syntax
int[] scores = new int[5];
String[] names = { "Emma", "James", "Sophia" };
Control Structures
Control flow constructs remain nearly identical between the languages:
// Conditional statements
if (temperature > 30)
{
Console.WriteLine("Hot day");
}
else if (temperature > 20)
{
Console.WriteLine("Pleasant weather");
}
else
{
Console.WriteLine("Cold day");
}
// Iteration constructs
for (int i = 0; i < 10; i++)
{
Console.WriteLine($"Iteration {i}");
}
while (keepRunning)
{
// Process loop
}
// Pattern matching switch
switch (dayOfWeek)
{
case DayOfWeek.Monday:
case DayOfWeek.Tuesday:
case DayOfWeek.Wednesday:
Console.WriteLine("Mid-week");
break;
default:
Console.WriteLine("Other day");
break;
}
Exception Handling
Exception handling follows identical patterns:
try
{
ProcessFile("data.txt");
}
catch (FileNotFoundException fnf)
{
Console.WriteLine($"File missing: {fnf.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"Unexpected error: {ex.Message}");
}
finally
{
ReleaseResources();
}
Comments
All three comment styles work identically:
// Single-line comment
/* Multi-line
comment block */
/// <summary>
/// XML documentation comment (similar to Javadoc)
/// </summary>
Variables and Data Types
Understanding C#'s type system helps transition smoothly from Java.
Variable Declaration
int age = 25;
string fullName = "Alice";
bool isActive = true;
int age = 25;
String fullName = "Alice";
boolean isActive = true;
Note that C# uses lowercase string and bool, while Java requires uppercase String and boolean.
Value Types vs Reference Types
C# maintains a clearer distinction between value and reference types:
Value Types (stored directly on the stack):
- Primitives:
int,float,double,bool,char - Custom
structtypes - Enumerations
Reference Types (stored on the heap, with reference on stack):
- Classes
- Interfaces
- Delegates
- Arrays
- Strings (immutable reference type with value semantics)
Nullable Types
C# introduces nullable value types, allowing primitives to hold null:
int? optionalCount = null;
bool? isValid = null;
// Checking nullable values
if (optionalCount.HasValue)
{
int value = optionalCount.Value;
}
This proves invaluable when working with databases or optional user input.
Implicit Typing with var
var productCount = 150; // Inferred as int
var customerName = "Acme"; // Inferred as string
var isComplete = false; // Inferred as bool
Java introduced var in version 10, but with more restrictions on usage contexts.
Constants
C# uses const for compile-time constants:
const int MaxRetry = 3;
const string DefaultLocale = "en-US";
Java prefers final for this purpose:
final int MAX_RETRY = 3;
final String DEFAULT_LOCALE = "en-US";
Enumerations
C# enums offer more flexibility with explicit underlying types:
public enum Priority : byte
{
Low = 0,
Medium = 5,
High = 10
}
public enum Status
{
Pending,
Processing,
Completed
}
Java enum are more object-oriented, essentially becoming special classes:
public enum Priority {
LOW(0),
MEDIUM(5),
HIGH(10);
private final int value;
Priority(int value) {
this.value = value;
}
}
Type Conversions
C# provides multiple conversion mechanisms:
// Implicit conversion (safe narrowing)
int smallNumber = 42;
long largeNumber = smallNumber;
// Explicit conversion (casting)
double precise = 3.14159;
int truncated = (int)precise;
// Using Convert class
string numeric = "123";
int converted = Convert.ToInt32(numeric);
// Parse method
double parsed = double.Parse("2.718");
// Safe TryParse pattern
string input = "999";
if (int.TryParse(input, out int result))
{
Console.WriteLine($"Parsed: {result}");
}
else
{
Console.WriteLine("Invalid number");
}
Operators and Expressions
Most operators work identically, but C# includes several unique additions.
Arithmetic Operators
int a = 17, b = 5;
int sum = a + b; // 22
int difference = a - b; // 12
int product = a * b; // 85
int quotient = a / b; // 3 (integer division)
int remainder = a % b; // 2
Assignment Operators
int x = 10;
x += 4; // 14
x -= 3; // 11
x *= 2; // 22
// Null-coalescing assignment (C# 8.0+)
string username = null;
username ??= "Anonymous"; // Assigns "Anonymous" if null
Comparison and Logical Operators
bool isAdult = age >= 18;
bool canVote = isCitizen && hasRegistered;
bool isWeekend = day == Day.Saturday || day == Day.Sunday;
Bitwise Operations
int flags = 0b1100; // Binary 12
int mask = 0b1010; // Binary 10
int andResult = flags & mask; // 8 (1000)
int orResult = flags | mask; // 14 (1110)
int xorResult = flags ^ mask; // 6 (0110)
int shifted = flags << 1; // 24 (11000)
Null-Coalescing Operators
// ?? returns the left operand unless it's null
string config = null;
string setting = config ?? "default"; // "default"
// ?. safe member access
Person person = null;
int? length = person?.Name?.Length; // null (no exception)
Pattern Matching
C# 7.0+ introduced enhanced pattern matching:
object data = 42;
if (data is int number)
{
Console.WriteLine($"Integer value: {number}");
}
// Switch pattern matching
switch (shape)
{
case Circle c when c.Radius > 10:
area = Math.PI * c.Radius * c.Radius;
break;
case Rectangle r:
area = r.Width * r.Height;
break;
default:
area = 0;
break;
}
Switch Expressions (C# 8.0+)
string GetDayType(int day) => day switch
{
1 or 2 or 3 => "Early week",
4 or 5 => "Midweek",
_ => "Weekend"
};
Lambda Expressions
// Func<T, TResult> for transformations
Func<int, int> square = x => x * x;
int squared = square(7); // 49
// Action<T> for void operations
Action<string> printer = msg => Console.WriteLine(msg);
// Complex lambda with multiple parameters
Func<int, int, string> formatter = (a, b) => $"{a} + {b} = {a + b}";
Expression-bodied Members
Simplify single-expression members:
public class Rectangle
{
public double Width { get; set; }
public double Height { get; set; }
public double Area => Width * Height;
public double Perimeter => 2 * (Width + Height);
public double CalculateDiagonal() => Math.Sqrt(Width * Width + Height * Height);
}
String Interpolation
C# string interpolation offers readable formatting:
string name = "Marcus";
int score = 95;
string report = $"Player {name} achieved {score} points.";
// Complex expressions
double radius = 5;
string circleInfo = $"Circumference: {2 * Math.PI * radius:F2}";
Control Flow Statements
Both languages share similar control structures, but C# adds modern pattern matching capabilities.
Pattern Matching with if
object response = FetchResponse();
if (response is string message && message.Length > 0)
{
Console.WriteLine($"Message received: {message}");
}
else if (response is int code)
{
Console.WriteLine($"Status code: {code}");
}
Advanced switch Features
// Range pattern matching
switch (temperature)
{
case < 0:
Console.WriteLine("Freezing");
break;
case >= 0 and < 20:
Console.WriteLine("Cold");
break;
case >= 20 and < 30:
Console.WriteLine("Comfortable");
break;
default:
Console.WriteLine("Hot");
break;
}
Loop Variations
// Traditional for loop
for (int i = 0; i < items.Count; i++)
{
Process(items[i]);
}
// foreach with LINQ filtering
foreach (var item in items.Where(i => i.IsActive))
{
Process(item);
}
// Collection initialization
var primes = new List<int> { 2, 3, 5, 7, 11 };
Exception Filters
try
{
ProcessOrder(order);
}
catch (OrderException ex) when (ex.Severity == ErrorSeverity.Critical)
{
NotifyAdmin(ex);
LogCritical(ex);
}
catch (OrderException ex) when (ex.Severity == ErrorSeverity.Warning)
{
LogWarning(ex);
}
Resource Management with using
Traditional using statement:
using (var reader = new StreamReader("data.csv"))
{
var content = reader.ReadToEnd();
Process(content);
}
Modern using declaration (C# 8.0+):
using var reader = new StreamReader("data.csv");
var content = reader.ReadToEnd();
// Resource automatically disposed at end of scope
Methods and Parameters
Basic Method Definition
public int CalculateTotal(int quantity, decimal unitPrice)
{
return quantity * (int)unitPrice;
}
Parameter Modifiers
C# provides multiple parameter passing strategies:
public void ModifyValue(int value)
{
value = 100; // Changes local copy only
}
public void ModifyRef(ref int value)
{
value = 100; // Changes caller's variable
}
public void RetrieveResult(int input, out int result)
{
result = input * 2; // Provides output value
}
public void SumAll(params int[] values)
{
Console.WriteLine($"Received {values.Length} arguments");
}
// Usage
int original = 50;
ModifyRef(ref original);
Console.WriteLine(original); // 100
int output;
RetrieveResult(21, out output); // output = 42
SumAll(1, 2, 3, 4, 5); // 5 arguments
Optional and Named Parameters
public void SendEmail(
string recipient,
string subject = "No Subject",
bool isUrgent = false)
{
Console.WriteLine($"To: {recipient}, Subject: {subject}");
}
// Various call patterns
SendEmail("user@example.com");
SendEmail("user@example.com", "Meeting Tomorrow");
SendEmail("user@example.com", isUrgent: true);
SendEmail(recipient: "user@example.com", subject: "Update");
Local Functions
Encapsulate helper logic within methods:
public int ComputeFactorial(int n)
{
int FactorialHelper(int current, int accumulator)
{
return current == 0 ? accumulator :
FactorialHelper(current - 1, current * accumulator);
}
return n < 0 ? 1 : FactorialHelper(n, 1);
}
Async Methods
public async Task<string> FetchUserDataAsync(int userId)
{
using var client = new HttpClient();
var response = await client.GetStringAsync(
$"https://api.example.com/users/{userId}");
return response;
}
// Invocation
string data = await FetchUserDataAsync(123);
Extension Methods
Add functionality to existing types without inheritance:
public static class StringExtensions
{
public static bool IsValidEmail(this string input)
{
return input.Contains('@') && input.Contains('.');
}
public static string Truncate(this string input, int maxLength)
{
return input.Length <= maxLength ?
input : input.Substring(0, maxLength) + "...";
}
}
// Usage
string email = "user@domain.com";
bool valid = email.IsValidEmail();
string shortened = "Long text content".Truncate(10);
Generic Methods
public T FindMaximum<T>(T first, T second) where T : IComparable<T>
{
return first.CompareTo(second) > 0 ? first : second;
}
// Usage
int maxInt = FindMaximum(42, 17);
string maxStr = FindMaximum("apple", "banana");
Method Composition
Functional programming support through delegate composition:
Func<int, int> doubleValue = x => x * 2;
Func<int, int> addFive = x => x + 5;
// Compose operations
Func<int, int> pipeline = x => addFive(doubleValue(x));
int result = pipeline(10); // (10 * 2) + 5 = 25
// Or using built-in composition methods
Func<int, int> composed = doubleValue.Compose(addFive);