Skip to main content

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

Comparing Structures and Classes
#

1. Core Concepts
#

  • Concept Explanation: In Swift, struct and class are general-purpose building blocks of your code. Both can define Properties to store values and Methods to provide functionality. Although the syntax is similar, Classes have additional capabilities: inheritance, runtime type casting, deinitializers, and reference counting. However, Swift officially recommends using Structs by default unless you specifically need inheritance or reference semantics.
  • Key Syntax: struct, class
  • Note:

Traditionally, an instance of a Class is called an Object. However, because Structs and Classes in Swift are so functionally similar, the official documentation generally uses the term “Instance” for both.

2. Example
#

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 property types as Int based on 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#
#

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

C# Example:

struct Resolution {
    public int Width;
    public int Height;    
}

class VideoMode {
    public Resolution Resolution = new Resolution();
    public bool Interlaced = false;
    public double FrameRate = 0.0;
    public string? Name; 
}

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 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: After defining types, 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 (.).

2. Example
#

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 directly read and assigned via dot syntax.

3. C#
#

Concept Correspondence: This is consistent with object creation and property access in C#.

C# Example:

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

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

someVideoMode.Resolution.Width = 1280;

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,” allowing you to specify values for properties directly during initialization. Classes do not have this feature.

2. Example
#

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

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

3. C#
#

Concept Correspondence: C# class or struct typically requires writing manual constructors or using Object Initializer syntax.

C# Example:

// 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#’s Object Initializer uses braces for property assignment { Width = 640 }.
  • Behavior: Swift’s auto-generated initializer enforces parameter passing (unless properties have default values), whereas C#’s Object Initializer ({ }) merely sets properties after the object is created, which is semantically different. Swift Classes do not automatically get this initializer and must define init manually.

Structures and Enumerations Are Value Types
#

1. Core Concepts
#

  • Concept Explanation: Value Types are types whose content is Copied when assigned to a variable, constant, or 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.
  • Note:

Collections in the Swift Standard Library (like Array, Dictionary, String) use a Copy-on-Write optimization technique. Actual memory copying only occurs when the data is modified; otherwise, memory is shared to ensure performance.

2. Example
#

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#
#

Concept Correspondence: C# struct is also a Value Type, and behaves the same way.

Key Differences Analysis:

  • Behavior:
    • String Difference: C#’s string is a Reference Type, but in Swift, String is a Struct (Value Type).
    • Collection Difference: C#’s List<T> or Dictionary<TKey, TValue> are Classes (Reference Types). If you assign a List to another variable, modifying one affects the other. However, in Swift, Array and Dictionary are Structs, so assignment creates a copy, and modifications are independent.

Classes Are Reference Types
#

1. Core Concepts
#

  • Concept Explanation: Reference Types are not copied when assigned or passed; instead, a reference to the memory address is passed. Multiple variables can point to the same instance simultaneously. Swift’s class is a Reference Type.

2. Example
#

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 point to the same VideoMode instance. Therefore, modifying frameRate via alsoTenEighty changes the value seen by tenEighty.

3. C#
#

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


Identity Operators
#

1. Core Concepts
#

  • Concept Explanation: Because Classes are reference types, sometimes we need to know if two variables “point to the exact same memory instance.” Swift provides === (Identical to) and !== (Not identical to) for this purpose.
  • Key Syntax: ===, !==

2. Example
#

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 implementing the Equatable protocol).

3. C#
#

Concept Correspondence: This is equivalent to Object.ReferenceEquals(a, b) in C#, or the default Reference comparison when the == operator is not overridden.

C# Example:

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 == defaults to reference comparison for Reference Types but compares values for string or overloaded types, which can be confusing. Swift’s design is more explicit.