Created
August 8, 2025 06:20
-
-
Save markylaredo/5df019eeb9a8876ee2c80af426bb5584 to your computer and use it in GitHub Desktop.
C# — Value vs Reference Types — Animated SVG Demo
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!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