Skip to content

Instantly share code, notes, and snippets.

@markylaredo
Created August 8, 2025 06:20
Show Gist options
  • Select an option

  • Save markylaredo/5df019eeb9a8876ee2c80af426bb5584 to your computer and use it in GitHub Desktop.

Select an option

Save markylaredo/5df019eeb9a8876ee2c80af426bb5584 to your computer and use it in GitHub Desktop.
C# — Value vs Reference Types — Animated SVG Demo
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>C# — Value vs Reference Types — Animated SVG Demo</title>
<style>
:root{--bg:#0f1724;--card:#0b1220;--accent:#7dd3fc;--muted:#94a3b8;--glass:rgba(255,255,255,0.03)}
html,body{height:100%;margin:0;font-family:Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,'Helvetica Neue',Arial; background:linear-gradient(180deg,#071028 0%, #061226 100%);color:#e6eef6}
.wrap{max-width:1100px;margin:28px auto;padding:20px}
h1{font-size:20px;margin:0 0 10px}
p{color:var(--muted);margin:6px 0 14px;line-height:1.45}
.card{background:var(--card);padding:18px;border-radius:12px;width:fullscreen; box-shadow:0 6px 30px rgba(2,6,23,0.7)}
.grid{display:grid;grid-template-columns:1fr 80px;gap:18px}
pre{background:var(--glass);padding:12px;border-radius:8px;overflow:auto;color:#cfe9ff}
code{font-family:ui-monospace, SFMono-Regular, Menlo, Monaco, 'Roboto Mono', 'Segoe UI Mono', monospace;font-size:13px}
.controls{display:flex;gap:8px;margin-bottom:10px}
button{background:transparent;border:1px solid rgba(125,211,252,0.12);padding:8px 10px;border-radius:8px;color:var(--accent);cursor:pointer}
button.primary{background:linear-gradient(90deg, rgba(125,211,252,0.12), rgba(99,102,241,0.04));border-color:rgba(125,211,252,0.22)}
.legend{font-size:13px;color:var(--muted);margin-top:8px}
.footer{margin-top:12px;color:var(--muted);font-size:13px}
/* small responsive tweak */
@media (max-width:980px){.grid{grid-template-columns:1fr;}}
/* SVG styles */
svg{width:100%;height:420px;display:block}
.varBox{fill:#071229;stroke:#18304a;rx:10;ry:10}
.heapBox{fill:#071526;stroke:#2b3950;rx:12;ry:12}
.valueText{font-weight:700;fill:#bfe9ff}
.label{font-size:12px;fill:#8fb7cf}
.thin{stroke-width:2;stroke:#6fd3ff}
.faded{opacity:0.12}
.pointer{stroke:#7dd3fc;stroke-width:2;fill:none}
.glow{filter:url(#glow)}
.mutedText{fill:#7ea8bf;font-size:12px}
/* animated highlight */
.pulse{animation:pulse 900ms ease-in-out infinite}
@keyframes pulse{0%{transform:scale(1);opacity:1}50%{transform:scale(1.04);opacity:0.9}100%{transform:scale(1);opacity:1}}
</style>
</head>
<body>
<div class="wrap">
<h1>C# — Value Types vs Reference Types (detailed explanation + animated demo)</h1>
<p>This page contains a short but detailed explanation of value vs reference types in C#, followed by a live animated SVG that visually demonstrates the difference: copies for value types, shared objects for reference types, and how assignments affect each. Use the controls to step through the timeline.</p>
<div class="grid">
<div class="card">
<h2 style="font-size:16px;margin-bottom:6px">Explanation (important points)</h2>
<div style="display:flex;gap:14px;flex-direction:column">
<div>
<strong>Memory & semantics</strong>
<p class="mutedText">Value types store the actual data where the variable is located (commonly the stack or inline inside another object). Assigning a value type copies the data. Reference types store a reference (pointer) to an object that lives on the heap; assigning a reference copies the reference, not the object.</p>
</div>
<div>
<strong>Common value types</strong>
<p class="mutedText">`int`, `float`, `double`, `bool`, `char`, `struct` and `enum`. They have copy semantics: <code>int b = a;</code> produces a separate copy.</p>
</div>
<div>
<strong>Common reference types</strong>
<p class="mutedText">`class`, `interface`, `delegate`, `string` (immutable reference type), `object`. Example: <code>var p2 = p1;</code> results in both pointing to the same object.</p>
</div>
<div>
<strong>Boxing & unboxing</strong>
<p class="mutedText">When a value type is assigned to a variable of type <code>object</code> (or an interface it implements), it is "boxed" — a heap object is allocated and the value copied into it. Unboxing extracts the value back (with a cast).</p>
</div>
<div>
<strong>Mutability & pitfalls</strong>
<p class="mutedText">Structs are value types — if they contain mutable fields, copying them can be surprising. Strings are reference types but immutable, which often makes them behave like value types from a programmer perspective.</p>
</div>
<div>
<strong>Short C# examples</strong>
<pre><code>// Value type: copy semantics
int a = 5;
int b = a; // b is a copy of a
b = 10; // a is still 5
// Reference type: shared object
class Person { public string Name; }
Person p1 = new Person { Name = "Alice" };
Person p2 = p1; // p2 reference copies the pointer
p2.Name = "Bob"; // p1.Name is now "Bob" too
// Reassigning p2 doesn't change p1
p2 = new Person { Name = "Charlie" };
// p1 still points to the object with Name = "Bob"
</code></pre>
</div>
<div class="footer">Tip: prefer immutable types where possible; avoid mutable structs. When performance matters, measure — large structs cause copying overhead.</div>
</div>
</div>
</div>
<div class="grid">
<div>
<div class="card" style="padding:12px;">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px">
<div style="font-weight:700;color:#dff7ff">Interactive demo</div>
<div class="controls">
<button id="playBtn" class="primary">Play</button>
<button id="pauseBtn">Pause</button>
<button id="prevBtn">Prev</button>
<button id="nextBtn">Next</button>
<button id="resetBtn">Reset</button>
</div>
</div>
<svg viewBox="0 0 980 420" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="4" result="coloredBlur"/>
<feMerge>
<feMergeNode in="coloredBlur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#7dd3fc"/>
</marker>
</defs>
<!-- Value types area -->
<g transform="translate(20,20)">
<text class="label" x="0" y="14">Value types (stack / inline)</text>
<!-- a box -->
<rect class="varBox" x="20" y="50" width="160" height="72"/>
<text class="label" x="28" y="72">a (int)</text>
<text id="valA" class="valueText" x="100" y="100" text-anchor="middle">5</text>
<!-- b box -->
<rect class="varBox" x="220" y="50" width="160" height="72"/>
<text class="label" x="228" y="72">b (int)</text>
<text id="valB" class="valueText" x="300" y="100" text-anchor="middle">5</text>
<!-- explanatory lines -->
<text class="mutedText" x="12" y="150">Assignment: <tspan style="font-weight:700">b = a;</tspan> => copy the value</text>
<!-- animation arrow (visual copy) -->
<path id="copyArrow" d="M140 120 C 180 150 220 150 260 120" stroke="#6fd3ff" stroke-width="2" fill="none" marker-end="url(#arrow)" opacity="0.0"/>
<rect x="20" y="140" width="360" height="1" class="faded"/>
</g>
<!-- Reference types area -->
<g transform="translate(410,20)">
<text class="label" x="0" y="14">Reference types (heap + references)</text>
<!-- p1 box -->
<rect class="varBox" x="20" y="50" width="160" height="72"/>
<text class="label" x="28" y="72">p1 (Person)</text>
<text id="refP1" class="mutedText" x="100" y="100" text-anchor="middle">(reference)</text>
<!-- p2 box -->
<rect class="varBox" x="220" y="50" width="160" height="72"/>
<text class="label" x="228" y="72">p2 (Person)</text>
<text id="refP2" class="mutedText" x="300" y="100" text-anchor="middle">null</text>
<!-- heap object -->
<rect id="heapObj" class="heapBox" x="120" y="160" width="220" height="96" rx="12" ry="12"/>
<text id="heapLabel" class="mutedText" x="230" y="186" text-anchor="middle">new Person()</text>
<text id="objName" class="valueText" x="230" y="214" text-anchor="middle">Name: Alice</text>
<!-- arrows from p1 and p2 to heap -->
<path id="arrowP1" class="pointer" d="M100 122 C 120 140 140 150 160 160" marker-end="url(#arrow)" opacity="1"/>
<path id="arrowP2" class="pointer" d="M300 122 C 280 140 260 150 240 160" marker-end="url(#arrow)" opacity="0"/>
<text class="mutedText" x="6" y="280">Assignment: <tspan style="font-weight:700">p2 = p1;</tspan> => copies reference (pointer)</text>
</g>
<!-- Footer explanation inside svg -->
<g transform="translate(20,320)">
<text class="mutedText" x="0" y="12">Use the controls to play a timeline that shows: copy for value types, shared object for reference types, mutating via one reference affects the other, reassigning a reference only changes that variable.</text>
</g>
</svg>
</div>
<div class="legend card" style="margin-top:12px;padding:12px">
<div style="font-weight:700;margin-bottom:6px">Legend & controls</div>
<div class="mutedText">• Press <strong>Play</strong> to run the timeline automatically. Use <strong>Next</strong>/<strong>Prev</strong> to step manually. <br>• Watch how the value of <code>b</code> changes independently, while <code>p1</code> and <code>p2</code> may point to the same heap object.</div>
</div>
</div>
</div>
</div>
<script>
// Simple timeline controller for the SVG demo
const steps = [
// Step 0: initial state
function step0(){
// value types
setText('valA','5'); setText('valB','5');
setOpacity('copyArrow',0);
// reference types
setText('refP1','(reference)'); setText('refP2','null');
setText('heapLabel','Person @ heap'); setText('objName','Name: Alice');
setOpacity('arrowP1',1); setOpacity('arrowP2',0);
},
// Step 1: show b = a copy arrow then finished copy
function step1(){
setOpacity('copyArrow',1);
// briefly animate arrow and then keep b as copy
setTimeout(()=>{ setOpacity('copyArrow',0); }, 700);
},
// Step 2: change b -> 10 (value types independent)
function step2(){
animateNumber('valB',5,10,500);
},
// Step 3: reference: p2 = p1 (p2 gets reference)
function step3(){
setText('refP2','(reference)');
setOpacity('arrowP2',1);
},
// Step 4: mutate through p2 -> Name = "Bob" (shared object)
function step4(){
setText('objName','Name: Bob');
// flash heap to indicate mutation
pulseElement('heapObj', 800);
},
// Step 5: p2 assigned a new object => p2 points to new object; p1 still points to old
function step5(){
// create a second heap object visually by relocating arrowP2
setText('objName','Name: Bob');
// move arrowP2 away and make p2 point to a new object rendered as small box
// for simplicity we'll change p2 label to reference(new Person)
setText('refP2','(reference -> new)');
// indicate p2 no longer points at the old heap
setOpacity('arrowP2',0.25);
// show a small new object text near p2
// we'll temporarily add text to simulate new heap
createFloatingNewObject();
},
// Step 6: final explanation state (no animation)
function step6(){
// leave things as they are
}
];
let idx = 0; let timer = null;
const playBtn = document.getElementById('playBtn');
const pauseBtn = document.getElementById('pauseBtn');
const nextBtn = document.getElementById('nextBtn');
const prevBtn = document.getElementById('prevBtn');
const resetBtn = document.getElementById('resetBtn');
function setText(id,txt){ const el = document.getElementById(id); if(el) el.textContent = txt; }
function setOpacity(id,o){ const el = document.getElementById(id); if(el) el.style.opacity = o; }
function animateNumber(id,from,to,duration){
const el = document.getElementById(id); if(!el) return;
const start = performance.now();
function frame(now){
let t = Math.min(1,(now-start)/duration);
const v = Math.round(from + (to-from)*t);
el.textContent = v;
if(t<1) requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
}
function pulseElement(id,ms){
const el = document.getElementById(id); if(!el) return;
el.style.transition = `filter ${ms/1000}s ease`;
el.style.filter = 'url(#glow)';
setTimeout(()=>{ el.style.filter = ''; }, ms);
}
function createFloatingNewObject(){
// Add a temporary text element near p2 to show new object
const svg = document.querySelector('svg');
if(!svg) return;
// remove existing if present
const existing = document.getElementById('newObjText'); if(existing) existing.remove();
const ns = 'http://www.w3.org/2000/svg';
const t = document.createElementNS(ns,'text');
t.setAttribute('id','newObjText'); t.setAttribute('x','320'); t.setAttribute('y','236');
t.setAttribute('class','valueText'); t.setAttribute('text-anchor','middle');
t.textContent = 'new Person()\nName: Charlie';
svg.appendChild(t);
// fade in
t.style.opacity = 0; t.style.transition = 'opacity 400ms ease';
setTimeout(()=>{ t.style.opacity = 1; },20);
}
function runStep(i){ if(i<0) i=0; if(i>=steps.length) i=steps.length-1; idx = i; steps[i](); }
// controls
playBtn.addEventListener('click', ()=>{
if(timer) return;
timer = setInterval(()=>{
if(idx < steps.length-1){ idx++; runStep(idx);} else { clearInterval(timer); timer=null; }
}, 1100);
});
pauseBtn.addEventListener('click', ()=>{ if(timer){ clearInterval(timer); timer=null; } });
nextBtn.addEventListener('click', ()=>{ if(idx < steps.length-1) runStep(++idx); });
prevBtn.addEventListener('click', ()=>{ if(idx > 0) runStep(--idx); });
resetBtn.addEventListener('click', ()=>{ if(timer){ clearInterval(timer); timer=null; } idx=0; // remove newObjText if exists
const e = document.getElementById('newObjText'); if(e) e.remove(); runStep(0);
});
// initialize
runStep(0);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment