Skip to main content

[From C# to Swift] 11. Methods

Learning Swift from a C# Perspective

Swift : Methods

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.

  1. increment(): Takes no arguments; directly accesses and modifies the internal count property.
  2. increment(by:): Demonstrates Swift’s parameter label (argument label) feature, making the call site highly readable.
  3. 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 func keyword, 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 code counter.increment(by: 5) read like a natural language sentence, a level of fluidity that C# Named Arguments cannot 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 write self explicitly 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 (self vs this), usage is identical.
  • Behavior: No significant difference. Swift official guidelines suggest not writing self unless necessary, which is similar to the C# community habit of usually avoiding explicit this unless 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 self or its properties. If modification is required, you must add the mutating keyword before the method declaration.
  • Key Syntax: mutating
  • Official Note:

Note: You cannot call a mutating method 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 error

Logic 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 any mutating methods.
    • C# Pitfall: In C#, if a Struct is stored in a readonly field, 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 explicit mutating and let/var mechanisms.

Assigning to self Within a Mutating Method
#

1. Core Concepts
#

  • Concept Explanation: In a mutating method, you can not only modify properties but also directly assign a brand new instance to the implicit self property. 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 mutating methods, enabling state management logic to be fully encapsulated within the type, so the caller only needs switch.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 static keyword.
    • For Classes, you can use static (cannot be overridden by subclasses) or class (allows overriding by subclasses).
  • 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:
    • static vs class: This is the most important point for C# developers.
      • Swift’s static func in a Class is equivalent to C#’s static (Final, cannot be overridden).
      • Swift’s class func in a Class corresponds to a concept C# does not have directly (think of it as an “inheritable static method”), allowing subclasses to override class func to provide their own implementation.
  • Behavior:
    • Self Reference: In Swift’s type methods, self refers to “The Type” itself, not an instance. Unlike C# where this cannot be used in static methods, Swift’s usage of self here allows you to access other static properties or methods, providing higher syntactic consistency.