Porting a C# Class
Step-by-step walkthrough of porting a C# class to sharp-runtime.
The C# Original
We will port this simple C# class:
// C# original
using System;
using System.Collections.Generic;
namespace MyApp
{
public class Player
{
public string Name { get; set; }
public int Score { get; private set; }
private List<string> achievements_ = new();
public event EventHandler<ScoreChangedArgs> ScoreChanged;
public Player(string name)
{
Name = name;
Score = 0;
}
public void AddScore(int points)
{
Score += points;
ScoreChanged?.Invoke(this, new ScoreChangedArgs(Score));
}
public void AddAchievement(string name)
{
achievements_.Add(name);
}
public override string ToString()
{
return $"{Name}: {Score} pts";
}
}
public class ScoreChangedArgs : EventArgs
{
public int NewScore { get; }
public ScoreChangedArgs(int score) { NewScore = score; }
}
}
Step 1: Create the Header File
Map each C# element to its sharp-runtime equivalent:
// Player.hpp
#pragma once
#include <string>
#include <System/Object.hpp>
#include <System/EventHandler.hpp>
#include <System/EventArgs.hpp>
#include <System/Collections/Generic/List.hpp>
#include <SharpRuntime/Prop.hpp>
namespace MyApp {
class ScoreChangedArgs : public System::EventArgs {
public:
explicit ScoreChangedArgs(int score) : newScore_(score) {}
DGETTER(int, NewScore)
GetTypeNameHPP();
private:
int newScore_;
};
class Player : public System::Object {
public:
explicit Player(const std::string& name);
// Properties
DDATA(std::string, Name) // read-write
DGETTER(int, Score) // read-only from outside
// Event
System::EventHandler<ScoreChangedArgs> ScoreChanged;
// Methods
void AddScore(int points);
void AddAchievement(const std::string& name);
std::string ToString() const override;
GetTypeNameHPP();
private:
int score_ = 0;
System::Collections::Generic::List<std::string> achievements_;
};
} // namespace MyApp
Step 2: Create the Implementation File
// Player.cpp
#include "Player.hpp"
#include <System/String.hpp>
namespace MyApp {
// ScoreChangedArgs
IGETTER(ScoreChangedArgs, int, NewScore, newScore_)
GetTypeNameCPP(ScoreChangedArgs, "MyApp.ScoreChangedArgs")
// Player
IDATA(Player, std::string, Name)
IGETTER(Player, int, Score, score_)
GetTypeNameCPP(Player, "MyApp.Player")
Player::Player(const std::string& name) : name_(name), score_(0) {}
void Player::AddScore(int points) {
score_ += points;
if (!ScoreChanged.Empty()) {
ScoreChanged.Raise(this, ScoreChangedArgs(score_));
}
}
void Player::AddAchievement(const std::string& name) {
achievements_.Add(name);
}
std::string Player::ToString() const {
return name_ + ": " + std::to_string(score_) + " pts";
}
} // namespace MyApp
Step 3: Usage
#include "Player.hpp"
#include <iostream>
int main() {
MyApp::Player player("Alice");
// Subscribe to event
player.ScoreChanged += [](System::Object* sender, MyApp::ScoreChangedArgs args) {
std::cout << "Score changed to: " << args.getNewScoreProperty() << "\n";
};
player.AddScore(50); // prints: Score changed to: 50
player.AddScore(25); // prints: Score changed to: 75
std::cout << player.ToString() << "\n"; // Alice: 75 pts
return 0;
}
Key Differences Summary
| C# construct | sharp-runtime equivalent |
|---|---|
public string Name { get; set; } | DDATA(std::string, Name) + IDATA() |
public int Score { get; private set; } | DGETTER(int, Score) + IGETTER() + private field |
new List<string>() | System::Collections::Generic::List<std::string> |
event EventHandler<T> E; | System::EventHandler<T> E; (public) |
E?.Invoke(this, args) | if (!E.Empty()) E.Raise(this, args); |
: EventArgs | : public System::EventArgs |
override string ToString() | std::string ToString() const override |
$"{name}: {score}" | name_ + ": " + std::to_string(score_) |