Skip to main content

[From C# to Swift] 09. Structures and Classes

Learning Swift from a C# Perspective

Swift : Structures and Classes

Comparing and Defining Structures and Classes
#

1. Core Concepts
#

  • Concept Explanation: In Swift, structures (struct) and classes (class) are general-purpose building blocks of your code. Both can define properties to store values and define methods to provide functionality. Unlike Objective-C or C++, Swift does not require you to create separate interface (.h) and implementation (.m) files; you define them in a single file. Although their syntax is similar, Classes have additional capabilities: inheritance, runtime type casting, deinitializers, and reference counting. However, Apple officially recommends preferring Structs by default, unless you specifically need the features unique to classes or require reference semantics.
  • Key Syntax: struct, class, Properties, Methods, Initializers
  • Official Note:

Traditionally, instances of a class are known as objects. However, Swift structures and classes are much closer in functionality than in other languages, and much of this chapter describes functionality that applies to instances of either a class or a structure type. Therefore, the more general term “instance” is used.

2. Example Analysis
#

Source Code:

struct Resolution {
    var width = 0
    var height = 0
}
class VideoMode {
    var resolution = Resolution()
    var interlaced = false
    var frameRate = 0.0
    var name: String?
}

Logic Explanation: This code defines a structure named Resolution to describe pixel resolution, and a class named VideoMode to describe video modes. Swift automatically infers the property types as Int based on the initial values (e.g., 0). The name property is defined as String? (Optional String), so it automatically receives a default value of nil.

3. C# Developer Perspective
#

Concept Mapping: This corresponds directly to struct and class definitions in C#.

C# Comparison Code:

struct Resolution {
    public int Width;
    public int Height;
    // C# structs are usually recommended to be Immutable; this is just for comparing Swift syntax
}

class VideoMode {
    public Resolution Resolution = new Resolution();
    public bool Interlaced = false;
    public double FrameRate = 0.0;
    public string? Name; // C# 8.0+ Nullable Reference Types
}

Key Differences Analysis:

  • Syntax:
    • Swift variable declarations use var (mutable) or let (constant), and do not require the public keyword to be accessible within the same module (default is internal).
    • Swift property declarations perform type inference directly from the initial value, making the syntax more concise than C#.
  • Behavior:
    • Swift Structs can implement Protocols (similar to C# Interfaces), have methods, Extensions, etc., possessing almost all capabilities of a Class except inheritance.
    • In C#, Structs are typically used for lightweight data encapsulation. In Swift, Structs are First-Class Citizens; even String, Array, and Dictionary in the standard library are Structs.

Structure and Class Instances & Accessing Properties
#

1. Core Concepts
#

  • Concept Explanation: Once types are defined, you need to create “instances” to use them. Swift uses initializer syntax to create instances; the simplest form is writing the type name followed by empty parentheses (). Properties are accessed using dot syntax (.).
  • Key Syntax: Instance, Dot Syntax, Initializer Syntax

2. Example Analysis
#

Source Code:

let someResolution = Resolution()
let someVideoMode = VideoMode()

print("The width of someResolution is \(someResolution.width)")
// Prints "The width of someResolution is 0".

someVideoMode.resolution.width = 1280
print("The width of someVideoMode is now \(someVideoMode.resolution.width)")
// Prints "The width of someVideoMode is now 1280".

Logic Explanation: Here, instances of Resolution and VideoMode are created, and their properties are accessed. Notably, even deep properties (like someVideoMode.resolution.width) can be read and assigned directly using dot syntax.

3. C# Developer Perspective
#

Concept Mapping: This is almost identical to object creation and property access in C#.

C# Comparison Code:

var someResolution = new Resolution();
var someVideoMode = new VideoMode();

Console.WriteLine($"The width of someResolution is {someResolution.Width}");

someVideoMode.Resolution.Width = 1280;

Key Differences Analysis:

  • Syntax:
    • Swift instance creation does not require the new keyword. This is the most common habit C# developers need to break.
    • Swift string interpolation uses \(variable), whereas C# uses $"{variable}".

Memberwise Initializers for Structure Types
#

1. Core Concepts
#

  • Concept Explanation: Swift Structs have a very convenient feature: if you do not define a custom initializer, the compiler automatically generates a “Memberwise Initializer” for you, allowing you to initialize member properties directly. Classes do not have this feature.
  • Key Syntax: Memberwise Initializer

2. Example Analysis
#

Source Code:

let vga = Resolution(width: 640, height: 480)

Logic Explanation: Since Resolution is a Struct, it automatically gains an initializer containing width and height parameters.

3. C# Developer Perspective
#

Concept Mapping: Standard C# struct (prior to C# 10) did not have this automatic behavior. C# 12 introduced Primary Constructors, and record struct can achieve similar results, but standard C# class or struct types usually require manually writing constructors or using Object Initializer syntax.

C# Comparison Code:

// C# Object Initializer syntax
var vga = new Resolution { Width = 640, Height = 480 };

// Or if a constructor is defined
// var vga = new Resolution(640, 480);

Key Differences Analysis:

  • Syntax: Swift’s memberwise initializer is in the form of function parameters (width: 640), whereas C# object initializers use curly braces for property assignment { Width = 640 }.
  • Behavior: Swift’s auto-generated initializer enforces parameter passing (unless properties have default values). C#’s Object Initializer ({ }) simply sets properties after object creation, which is semantically different. Swift Classes do not automatically get this initializer; you must manually define init.

Structures and Enumerations Are Value Types
#

1. Core Concepts
#

  • Concept Explanation: A Value Type is a type whose value is copied when it is assigned to a variable or constant, or when it is passed to a function. In Swift, all struct and enum types are Value Types. This includes Int, Double, Bool, String, Array, and Dictionary in the Swift Standard Library.
  • Key Syntax: Value Type, Copy
  • Official Note:

Collections defined by the Swift standard library like arrays, dictionaries, and strings use an optimization to reduce the performance cost of copying. Instead of making a copy immediately, these collections share the memory where the elements are stored. The actual copy is performed only when one of the copies is modified (Copy-on-Write).

2. Example Analysis
#

Source Code:

let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
cinema.width = 2048

print("cinema is now \(cinema.width) pixels wide")
// Prints "cinema is now 2048 pixels wide".

print("hd is still \(hd.width) pixels wide")
// Prints "hd is still 1920 pixels wide".

Logic Explanation: When hd is assigned to cinema, a copy occurs. Therefore, modifying the width of cinema does not affect the original hd at all. They are two independent instances in memory.

3. C# Developer Perspective
#

Concept Mapping: C# struct is also a Value Type, and behaves identically in this regard.

Key Differences Analysis:

  • Behavior (Crucial):
    • String Differences: C# string is a Reference Type (though its immutability makes it behave somewhat like a value type), but in Swift, String is a Struct (Value Type). This means Swift string passing involves copying behavior (logically), representing a significant conceptual shift for C# developers.
    • Collection Differences: C# List<T> or Dictionary<TKey, TValue> are Classes (Reference Types). If you assign a List to another variable, modifying one affects the other. In Swift, Array and Dictionary are Structs; assignment creates a copy, and modifications are independent. This is the most common pitfall for C# developers moving to Swift.

Classes Are Reference Types
#

1. Core Concepts
#

  • Concept Explanation: Reference Types are not copied when assigned or passed; instead, a reference to the same existing instance is used. Multiple variables can point to the same instance in memory. Swift class is a Reference Type.
  • Key Syntax: Reference Type, Shared Instance

2. Example Analysis
#

Source Code:

let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0

let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0

print("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
// Prints "The frameRate property of tenEighty is now 30.0".

Logic Explanation: tenEighty and alsoTenEighty refer to the same VideoMode instance. Therefore, modifying frameRate via alsoTenEighty also changes the value seen by tenEighty.

3. C# Developer Perspective
#

Concept Mapping: This corresponds exactly to C# class behavior.

Key Differences Analysis:

  • Syntax:
    • Note that the example uses let tenEighty = VideoMode() (constant). In Swift, this means the tenEighty pointer cannot change (it cannot point to a different object), but the content of the object it points to can be changed. This is similar to a readonly field holding a Reference Type in C#.
    • If VideoMode were a Struct, declaring it with let would prevent modification of any of its properties (because a Struct treats its content as a single value).

Identity Operators
#

1. Core Concepts
#

  • Concept Explanation: Because classes are reference types, it is sometimes necessary to determine if two variables “refer to exactly the same instance in memory.” Swift provides === (Identical to) and !== (Not identical to) for this purpose.
  • Key Syntax: ===, !==

2. Example Analysis
#

Source Code:

if tenEighty === alsoTenEighty {
    print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")
}

Logic Explanation: Here, === is used to check if two variables refer to the same VideoMode instance. Note that this is different from == (Equal to); == is typically used to compare if “values” are equal (requires conforming to the Equatable protocol).

3. C# Developer Perspective
#

Concept Mapping: This is equivalent to Object.ReferenceEquals(a, b) in C#, or the default behavior of the == operator on reference types when not overloaded.

C# Comparison Code:

if (Object.ReferenceEquals(tenEighty, alsoTenEighty)) {
    // ...
}
// Or for standard classes
if (tenEighty == alsoTenEighty) { }

Key Differences Analysis:

  • Syntax: Swift strictly distinguishes between “Reference Identity (===)” and “Value Equality (==)”. C#’s == compares references by default for Reference Types, but compares values for string or types that overload operators, which can be confusing. Swift’s design is more explicit.