Object Lifecycle

How objects are created, owned, shared, and destroyed — without garbage collection.

No Garbage Collector

Unlike .NET, sharp-runtime has no garbage collector. System::GC is a stub class that exists only for API compatibility — it does nothing. All memory management is explicit C++ RAII.

This is a fundamental difference from .NET. You must think about ownership when writing or porting code.

Ownership Patterns

PatternUse whenNotes
Stack valueShort-lived, single owner, value semanticsDestroyed at scope end
std::unique_ptr<T>Single owner, heap-allocatedOwnership transferable via move
std::shared_ptr<T>Shared ownership, multiple holdersRef-counted; cycles are a risk
Raw pointer / referenceBorrowed (non-owning)Caller must keep owner alive

Typical Object Creation

// Stack allocation (value type behavior)
System::DateTime dt = System::DateTime::Now();

// Heap with unique ownership
auto stream = std::make_unique<System::IO::MemoryStream>();

// Heap with shared ownership
auto obj = std::make_shared<MyClass>();
std::shared_ptr<MyClass> ref2 = obj; // both hold a reference
// obj and ref2 are both destroyed when count drops to 0

Virtual Destructor Rule

All classes inheriting from System::Object get a virtual destructor via virtual ~Object() = default. This ensures the correct destructor runs when deleting through a base pointer.

std::unique_ptr<System::Object> obj = std::make_unique<MyDerivedClass>();
// When obj goes out of scope, ~MyDerivedClass() runs correctly

IDisposable Pattern

Classes with external resources (file handles, sockets, etc.) implement System::IDisposable with a Dispose() method. Unlike C# using statements, there is no language-level RAII integration — you must call Dispose() manually or use a custom RAII wrapper.

// Manual dispose
auto fs = std::make_unique<System::IO::FileStream>("file.txt", ...);
// ... use ...
fs->Dispose(); // or let unique_ptr destructor run (if it calls Dispose)
C++ destructor vs .NET Dispose
In .NET, deterministic cleanup uses Dispose(); finalizers are a GC fallback. In sharp-runtime, the C++ destructor is the primary cleanup mechanism. Dispose() exists for API compatibility and may delegate to the destructor.

Copy and Move Semantics

sharp-runtime classes use default C++ copy/move semantics unless a class explicitly manages resources:

Null Handling

In C++ there is no concept of "null object" for value types. Reference semantics are represented by pointers:

Reference Cycles

Cycle Risk with shared_ptr
If object A holds a shared_ptr to B and B holds a shared_ptr back to A, neither will ever be destroyed. Break cycles with std::weak_ptr<T>. This is a common pitfall when porting C# code where the GC handles cycles automatically.