Porting C# Code
Systematic guide to porting .NET code to sharp-runtime.
Type Mappings
Primitive Types
| C# type | sharp-runtime alias | Underlying C++ type |
|---|---|---|
int | intcs | int32_t |
long | longcs | int64_t |
short | shortcs | int16_t |
byte | bytecs | uint8_t |
sbyte | sbytecs | int8_t |
uint | uintcs | uint32_t |
ulong | ulongcs | uint64_t |
ushort | ushortcs | uint16_t |
float | float | float |
double | double | double |
bool | bool | bool |
char | charcs | char16_t |
string | std::string | std::string |
object | System::Object* | pointer |
Include <SharpRuntime/SharpRuntimeHelper.hpp> for the aliases.
Collections
| C# type | sharp-runtime type |
|---|---|
T[] | std::vector<T> |
List<T> | System::Collections::Generic::List<T> |
Dictionary<K,V> | System::Collections::Generic::Dictionary<K,V> |
HashSet<T> | System::Collections::Generic::HashSet<T> |
Queue<T> | System::Collections::Generic::Queue<T> |
Stack<T> | System::Collections::Generic::Stack<T> |
Memory Management
Most Critical Difference
C# has garbage collection. sharp-runtime does not. Every object must have an explicit owner.
Failing to handle ownership leads to memory leaks or use-after-free bugs.
| C# pattern | sharp-runtime equivalent |
|---|---|
new MyClass() | std::make_unique<MyClass>() (single owner) |
| Shared reference | std::make_shared<MyClass>() |
using (var x = ...) | Destructor runs at scope end (RAII) |
x = null | ptr.reset() or let scope end |
x == null | ptr == nullptr or !ptr |
Properties
// C#
public int Width { get; set; }
public int Area => Width * Height; // computed
// sharp-runtime
// In .hpp:
DDATA(int, Width)
DGETTER(int, Area)
// In .cpp:
IDATA(MyClass, int, Width)
IGETTER(MyClass, int, Area, width_ * height_)
Events
// C#
public event EventHandler<MyArgs> Changed;
Changed?.Invoke(this, new MyArgs());
// sharp-runtime
System::EventHandler<MyArgs> Changed;
if (!Changed.Empty()) Changed.Raise(this, MyArgs{});
String Interpolation
// C#
string msg = $"Hello {name}, you are {age} years old.";
// sharp-runtime (option 1: String::Format)
std::string msg = System::String::Format("Hello {0}, you are {1} years old.", name, age);
// sharp-runtime (option 2: std::string concatenation)
std::string msg = "Hello " + name + ", you are " + std::to_string(age) + " years old.";
Null Coalescing
// C#
string name = input ?? "default";
int val = n ?? 0;
// sharp-runtime
std::string name = input.empty() ? "default" : input;
int val = n.GetValueOrDefault(0); // for Nullable<int> n
foreach and LINQ
// C# foreach
foreach (var item in items) { ... }
// sharp-runtime (range-for on std::vector or List<T>)
for (const auto& item : items) { ... }
// C# LINQ — no direct equivalent
// Use std::algorithm or manual loops
var names = players.Where(p => p.Score > 0).Select(p => p.Name).ToList();
// sharp-runtime equivalent
std::vector<std::string> names;
for (const auto& p : players) {
if (p.getScoreProperty() > 0)
names.push_back(p.getNameProperty());
}
Async/Await
C# async/await has no direct equivalent in sharp-runtime. Options:
- Use
std::threadandSystem::Threading::Thread - Use
System::Threading::Tasks::Task(partial implementation — needs verification) - Refactor to synchronous code where possible
Interfaces
// C#
public interface IDrawable { void Draw(); }
public class Sprite : IDrawable { public void Draw() { ... } }
// sharp-runtime (pure abstract base class)
class IDrawable {
public:
virtual ~IDrawable() = default;
virtual void Draw() = 0;
};
class Sprite : public IDrawable {
public:
void Draw() override { ... }
};
Exception Handling
// C# — works the same in sharp-runtime
try {
doSomething();
} catch (System::ArgumentException& e) {
std::cerr << e.what() << "\n";
} catch (System::Exception& e) {
std::cerr << "Error: " << e.what() << "\n";
}
Note: In C++, catch by reference (&). In C#, catch by type without &.