Introduction
In the world of programming, different languages often have their own unique features and functions that make them stand out. Ruby, a dynamically-typed scripting language known for its simplicity and elegance, has a handy function called times() that allows you to execute a block of code a specified number of times.
But what if you’re working in C#, particularly in the context of Unity game development? Is there a way to achieve the same functionality? In this blog post, we’ll explore how you can replicate Ruby’s times() function in C#/Unity.
Ruby’s times() Function – A Quick Refresher
Ruby’s times() method is elegant and concise:
5.times { |i| puts "Iteration #{i}" }
# Output:
# Iteration 0
# Iteration 1
# Iteration 2
# Iteration 3
# Iteration 4
This single line replaces what would otherwise require a traditional loop. The beauty of times() lies in its readability and the fact that it automatically handles the iteration counter.
Replicating times() in C#
C# doesn’t have a built-in times() method, but we can create one using extension methods and delegates. Here are several approaches:
Approach 1: Simple Extension Method
using System;
public static class IntegerExtensions
{
public static void Times(this int count, Action<int> action)
{
for (int i = 0; i < count; i++)
{
action(i);
}
}
}
// Usage in Unity:
void Start()
{
5.Times(i => Debug.Log($"Iteration {i}"));
}
This is the closest equivalent to Ruby’s times() and provides clean, readable code.
Approach 2: With Return Value (Select Equivalent)
If you need to collect results from each iteration:
using System;
using System.Collections.Generic;
public static class IntegerExtensions
{
public static IEnumerable<TResult> Times<TResult>(this int count, Func<int, TResult> selector)
{
var results = new List<TResult>();
for (int i = 0; i < count; i++)
{
results.Add(selector(i));
}
return results;
}
}
// Usage:
void Start()
{
var numbers = 5.Times(i => i * i); // [0, 1, 4, 9, 16]
foreach (var num in numbers)
{
Debug.Log(num);
}
}
Approach 3: Using LINQ (Unity 2021+)
For projects using modern .NET or Unity’s IL2CPP with LINQ support:
using System.Linq;
void Start()
{
Enumerable.Range(0, 5).Each(i => Debug.Log($"Iteration {i}"));
// Or with LINQ's built-in methods:
var results = Enumerable.Range(0, 5)
.Select(i => i * 2)
.ToList(); // [0, 2, 4, 6, 8]
}
⚠️ Note: LINQ may not be available in all Unity configurations. Check your project’s .NET version compatibility.
Practical Unity Examples
Example 1: Spawning Multiple Objects
using UnityEngine;
public class ObjectSpawner : MonoBehaviour
{
public GameObject prefab;
public int count = 10;
public float spacing = 2f;
void Start()
{
count.Times(i =>
{
Vector3 position = new Vector3(i * spacing, 0, 0);
Instantiate(prefab, position, Quaternion.identity);
});
}
}
Example 2: Creating UI Elements
using UnityEngine;
using UnityEngine.UI;
public class UISpawner : MonoBehaviour
{
public GameObject buttonPrefab;
public Transform parentPanel;
public int itemCount = 5;
void Start()
{
itemCount.Times(i =>
{
var button = Instantiate(buttonPrefab, parentPanel);
button.GetComponent<Text>().text = $"Item {i + 1}";
});
}
}
Example 3: Initializing Arrays or Lists
using System.Collections.Generic;
using UnityEngine;
public class ListInitializer : MonoBehaviour
{
void Start()
{
var scores = new List<int>();
// Initialize with default values
10.Times(i => scores.Add(0));
Debug.Log($"List has {scores.Count} elements"); // Output: 10
}
}
Example 4: Coroutine-Based Timing
Combine times() with Unity coroutines for timed sequences:
using System.Collections;
using UnityEngine;
public class TimedSequence : MonoBehaviour
{
void Start()
{
StartCoroutine(PlaySequence());
}
IEnumerator PlaySequence()
{
5.Times(i =>
{
Debug.Log($"Step {i + 1}");
// Note: This runs synchronously; for async timing, see below
});
yield return null;
}
}
// For timed steps between iterations:
IEnumerator PlayTimedSequence()
{
for (int i = 0; i < 5; i++)
{
Debug.Log($"Step {i + 1}");
yield return new WaitForSeconds(1f);
}
}
Comparison: Ruby vs C# Iteration Styles
| Feature | Ruby times() | C# Extension Method | Traditional Loop |
|---|---|---|---|
| Syntax | 5.times { |i| ... } | 5.Times(i => ...) | for (int i=0; i<5; i++) |
| Readability | Excellent | Very Good | Good |
| Flexibility | High | High | Highest |
| Performance | Good | Good | Best |
| Learning Curve | Easy | Moderate | Easy |
When to Use Each Approach
Use Extension Method times() When:
- You want clean, Ruby-like syntax in C#
- The iteration logic is simple and self-contained
- Code readability is a priority
Use Traditional Loop When:
- Performance is critical (micro-optimizations)
- You need early exit (
break) or skip (continue) - Complex nested iterations
Use LINQ When:
- You’re transforming data collections
- Your project supports modern .NET
- You prefer functional programming style
Best Practices
- Keep extension methods simple: Avoid complex logic inside
times()callbacks - Use meaningful variable names:
count.Times(i => ...)is clearer thann.Times(x => ...) - Be aware of performance: Extension methods add a tiny overhead; negligible for most games
- Document your extensions: Add XML comments so other developers understand the API
Related Resources
- Understanding Unity Coroutines – Time-based game mechanics
- How to Utilize yield return in Unity – Advanced coroutine patterns
- A Useful Countdown Timer Class in C# – Reusable timing utilities
Which iteration style do you prefer in your Unity projects? Share your thoughts in the comments! 💻✨