
Learning Swift from a C# Perspective
Instance Methods #
1. Core Concepts #
- Concept Explanation: Instance methods are functions that belong to instances of a particular type (Class, Structure, or Enumeration). They encapsulate logic for manipulating that instance. Unlike Objective-C, which only allows classes to define methods, Swift’s Structs and Enums can also define methods, making Swift’s Value Types extremely powerful.
- Key Syntax:
func,Class,Structure,Enumeration
2. Example Analysis #
Source Code:
class Counter {
var count = 0
func increment() {
count += 1
}
func increment(by amount: Int) {
count += amount
}
func reset() {
count = 0
}
}
let counter = Counter()
counter.increment()
counter.increment(by: 5)
counter.reset()Logic Explanation:
This code defines a Counter class containing three instance methods.
increment(): Takes no arguments; directly accesses and modifies the internalcountproperty.increment(by:): Demonstrates Swift’s parameter label (argument label) feature, making the call site highly readable.- Calls use dot syntax (
.), consistent with most object-oriented languages.
3. C# Developer Perspective #
Concept Correspondence: This corresponds exactly to Instance Methods in C#.
C# Comparison Code:
public class Counter {
public int Count { get; private set; } = 0;
public void Increment() {
Count += 1;
}
public void Increment(int amount) {
Count += amount;
}
public void Reset() {
Count = 0;
}
}Key Differences Analysis:
- Syntax: Swift uses the
funckeyword, whereas C# starts with the return type (e.g.,void). Swift’s parameter naming supports separating “external labels” from “internal names” (e.g.,increment(by amount: Int)). This makes the calling codecounter.increment(by: 5)read like a natural language sentence, a level of fluidity that C#Named Argumentscannot fully achieve. - Behavior: Basic behavior is consistent. However, in Swift, methods are defined on Structures (Struct) and Enumerations (Enum) much more frequently than in C#, because Swift’s Struct features are far richer than C#’s.
The self Property #
1. Core Concepts #
- Concept Explanation: Every instance has an implicit property called
self, representing the instance itself. usually, you don’t need to writeselfexplicitly unless a naming conflict occurs (e.g., when a parameter name is the same as a property name). - Key Syntax:
self
2. Example Analysis #
Source Code:
struct Point {
var x = 0.0, y = 0.0
func isToTheRightOf(x: Double) -> Bool {
return self.x > x
}
}Logic Explanation:
In the isToTheRightOf method, the parameter name is x, and the property name is also x. To eliminate ambiguity (Disambiguate), Swift uses self.x to refer to the instance property, while the standalone x refers to the parameter.
3. C# Developer Perspective #
Concept Correspondence: Equivalent to the this keyword in C#.
C# Comparison Code:
struct Point {
public double X;
public double Y;
public bool IsToTheRightOf(double x) {
// C# uses this to disambiguate
return this.X > x;
}
}Key Differences Analysis:
- Syntax: Except for the keyword spelling (
selfvsthis), usage is identical. - Behavior: No significant difference. Swift official guidelines suggest not writing
selfunless necessary, which is similar to the C# community habit of usually avoiding explicitthisunless necessary.
Modifying Value Types from Within Instance Methods #
1. Core Concepts #
- Concept Explanation: Swift’s Structures and Enumerations are Value Types. In non-mutating instance methods, you cannot modify
selfor its properties. If modification is required, you must add themutatingkeyword before the method declaration. - Key Syntax:
mutating - Official Note:
Note: You cannot call a
mutatingmethod on a constant (let) structure instance, because properties of a constant structure are completely immutable.
2. Example Analysis #
Source Code:
struct Point {
var x = 0.0, y = 0.0
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
x += deltaX
y += deltaY
}
}
var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveBy(x: 2.0, y: 3.0)
// Point is now at (3.0, 4.0)
let fixedPoint = Point(x: 3.0, y: 3.0)
// fixedPoint.moveBy(x: 2.0, y: 3.0) // This line will report an errorLogic Explanation:
The moveBy method modifies x and y. Since Point is a Struct, it must be marked as mutating. When the method ends, these changes are written back to the original structure.
3. C# Developer Perspective #
Concept Correspondence: C# Structs are mutable by default (unless declared as readonly struct). C# does not have a marker like mutating for individual methods.
C# Comparison Code:
struct Point {
public double X;
public double Y;
// C# struct methods can modify fields by default (although Mutable Structs are not recommended)
public void MoveBy(double deltaX, double deltaY) {
X += deltaX;
Y += deltaY;
}
}Key Differences Analysis:
- Syntax: Swift enforces explicit declaration of
mutating. This is an “intent declaration,” letting readers clearly know that this method will change the value state. C# does not have this keyword. - Behavior:
- Safety: Swift’s design is safer. If you declare
let p = Point(...), the compiler will forbid calling anymutatingmethods. - C# Pitfall: In C#, if a Struct is stored in a
readonlyfield, calling a method that modifies state might cause the compiler to create a “Defensive Copy.” The copy is modified instead of the original value, leading to subtle bugs. Swift eliminates this class of problems at compile time via explicitmutatingandlet/varmechanisms.
- Safety: Swift’s design is safer. If you declare
Assigning to self Within a Mutating Method #
1. Core Concepts #
- Concept Explanation: In a
mutatingmethod, you can not only modify properties but also directly assign a brand new instance to the implicitselfproperty. This is particularly useful in Enumeration (Enum) state switching logic. - Key Syntax:
self = ...
2. Example Analysis #
Source Code:
enum TriStateSwitch {
case off, low, high
mutating func next() {
switch self {
case .off:
self = .low
case .low:
self = .high
case .high:
self = .off
}
}
}Logic Explanation:
This is a “three-stage switch” enumeration. The next() method checks the current self state and directly replaces self with the next state (.low, .high, etc.). This is a concise way to implement a Finite State Machine (FSM).
3. C# Developer Perspective #
Concept Correspondence: C# Structs allow this = new Struct(...), but Enums cannot define methods, nor can they modify this.
C# Comparison Code:
// C# Enums are just integer aliases; they cannot contain methods or modify themselves.
// Must be simulated via Extension Methods, but cannot modify the original variable (because of pass-by-value).
public enum TriStateSwitch { Off, Low, High }
public static class SwitchExtensions {
public static TriStateSwitch Next(this TriStateSwitch s) {
return s switch {
TriStateSwitch.Off => TriStateSwitch.Low,
TriStateSwitch.Low => TriStateSwitch.High,
TriStateSwitch.High => TriStateSwitch.Off,
_ => TriStateSwitch.Off
};
}
}
// The caller must reassign
// mySwitch = mySwitch.Next();Key Differences Analysis:
- Syntax: Swift Enums are “First Class Citizens” that can possess state and behavior. C# Enums are merely numeric constants.
- Behavior: Swift allows Enums to self-mutate via
mutatingmethods, enabling state management logic to be fully encapsulated within the type, so the caller only needsswitch.next(). C# developers usually rely on Extension Methods and return new values, or move logic to an external class.
Type Methods #
1. Core Concepts #
- Concept Explanation: Methods that belong to the type itself rather than a single instance are called Type Methods.
- For Structs and Enums, use the
statickeyword. - For Classes, you can use
static(cannot be overridden by subclasses) orclass(allows overriding by subclasses).
- For Structs and Enums, use the
- Key Syntax:
static func,class func - Official Note:
Objective-C only allowed defining type methods on classes. Swift allows defining them on Classes, Structs, and Enums.
2. Example Analysis #
Source Code:
class SomeClass {
class func someTypeMethod() {
// Implement type method here
}
}
SomeClass.someTypeMethod()
struct LevelTracker {
static var highestUnlockedLevel = 1
var currentLevel = 1
static func unlock(_ level: Int) {
if level > highestUnlockedLevel { highestUnlockedLevel = level }
}
static func isUnlocked(_ level: Int) -> Bool {
return level <= highestUnlockedLevel
}
// ... (Instance methods omitted)
}Logic Explanation:
LevelTracker uses static to define the unlock and isUnlocked methods. These methods manage global game progress (the highest unlocked level) without relying on a specific player instance.
3. C# Developer Perspective #
Concept Correspondence: Equivalent to C# static methods.
C# Comparison Code:
class SomeClass {
// C# static methods cannot be overridden (unless using static abstract in interfaces - C# 11+)
public static void SomeTypeMethod() { }
}
struct LevelTracker {
public static int HighestUnlockedLevel = 1;
public static void Unlock(int level) {
if (level > HighestUnlockedLevel) HighestUnlockedLevel = level;
}
}Key Differences Analysis:
- Syntax:
staticvsclass: This is the most important point for C# developers.- Swift’s
static funcin a Class is equivalent to C#’sstatic(Final, cannot be overridden). - Swift’s
class funcin a Class corresponds to a concept C# does not have directly (think of it as an “inheritable static method”), allowing subclasses tooverride class functo provide their own implementation.
- Swift’s
- Behavior:
- Self Reference: In Swift’s type methods,
selfrefers to “The Type” itself, not an instance. Unlike C# wherethiscannot be used in static methods, Swift’s usage ofselfhere allows you to access other static properties or methods, providing higher syntactic consistency.
- Self Reference: In Swift’s type methods,