CodeJunkie.Observables
1.0.0
dotnet add package CodeJunkie.Observables --version 1.0.0
NuGet\Install-Package CodeJunkie.Observables -Version 1.0.0
<PackageReference Include="CodeJunkie.Observables" Version="1.0.0" />
<PackageVersion Include="CodeJunkie.Observables" Version="1.0.0" />
<PackageReference Include="CodeJunkie.Observables" />
paket add CodeJunkie.Observables --version 1.0.0
#r "nuget: CodeJunkie.Observables, 1.0.0"
#:package CodeJunkie.Observables@1.0.0
#addin nuget:?package=CodeJunkie.Observables&version=1.0.0
#tool nuget:?package=CodeJunkie.Observables&version=1.0.0
CodeJunkie.Observables
A lightweight and efficient observable data structures library for Unity and .NET, providing reactive programming capabilities with MVVM pattern support.
🎯 Features
- Observable Properties - Strongly-typed properties with change notifications
- Observable Collections - Lists and dictionaries with collection change events
- Cross-Platform - Works with Unity (.NET 2.0/4.6) and modern .NET versions
- Thread-Safe - All operations are thread-safe with proper locking mechanisms
- Memory Efficient - Cached event args and optimized allocations
- MVVM Ready - Full support for data binding patterns
📦 Core Components
ObservableProperty<T>
Provides property change notifications for individual values:
var health = new ObservableProperty<int>(100);
health.ValueChanged += (sender, e) => Console.WriteLine($"Health changed to: {health.Value}");
health.Value = 80; // Triggers ValueChanged event
ObservableList<T>
A dynamic list that provides notifications when items are added, removed, or changed:
var inventory = new ObservableList<string>();
inventory.CollectionChanged += (sender, e) => {
Console.WriteLine($"Collection action: {e.Action}");
};
inventory.Add("Sword"); // Triggers Add event
inventory.Remove("Sword"); // Triggers Remove event
inventory.Clear(); // Triggers Reset event
ObservableDictionary<TKey, TValue>
A dictionary implementation with collection change notifications:
var playerStats = new ObservableDictionary<string, int>();
playerStats.CollectionChanged += (sender, e) => {
Console.WriteLine($"Stats changed: {e.Action}");
};
playerStats["Strength"] = 15; // Triggers Add event
playerStats["Strength"] = 20; // Triggers Replace event
ObservableObject
Base class for creating observable view models:
public class PlayerViewModel : ObservableObject
{
private string _name;
private int _level;
public string Name
{
get => _name;
set => Set(ref _name, value); // Automatically raises PropertyChanged
}
public int Level
{
get => _level;
set => Set(ref _level, value);
}
}
🎮 Unity Game Development Examples
Player Health System
public class PlayerHealth : MonoBehaviour
{
private ObservableProperty<int> _health = new ObservableProperty<int>(100);
void Start()
{
// Bind to UI
_health.ValueChanged += OnHealthChanged;
}
void OnHealthChanged(object sender, EventArgs e)
{
var healthBar = FindObjectOfType<Slider>();
healthBar.value = _health.Value / 100f;
if (_health.Value <= 0)
{
GameOver();
}
}
public void TakeDamage(int damage)
{
_health.Value = Mathf.Max(0, _health.Value - damage);
}
}
Inventory Management
public class InventoryManager : MonoBehaviour
{
private ObservableList<Item> _items = new ObservableList<Item>();
void Start()
{
_items.CollectionChanged += OnInventoryChanged;
}
void OnInventoryChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (Item item in e.NewItems)
{
CreateItemUI(item);
}
break;
case NotifyCollectionChangedAction.Remove:
foreach (Item item in e.OldItems)
{
DestroyItemUI(item);
}
break;
}
}
public void AddItem(Item item) => _items.Add(item);
public void RemoveItem(Item item) => _items.Remove(item);
}
Game State Management
public class GameStateManager : ObservableObject
{
private GameState _currentState;
private ObservableDictionary<string, int> _playerStats = new ObservableDictionary<string, int>();
public GameState CurrentState
{
get => _currentState;
set => Set(ref _currentState, value);
}
public ObservableDictionary<string, int> PlayerStats => _playerStats;
void Start()
{
PropertyChanged += OnPropertyChanged;
_playerStats.CollectionChanged += OnStatsChanged;
}
void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(CurrentState))
{
HandleStateChange();
}
}
void OnStatsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
UpdateStatsUI();
}
}
🔧 Advanced Usage
Custom Observable Properties
public class ClampedObservableProperty<T> : ObservableProperty<T> where T : IComparable<T>
{
private T _min;
private T _max;
public ClampedObservableProperty(T value, T min, T max) : base(value)
{
_min = min;
_max = max;
}
public override T Value
{
get => base.Value;
set
{
var clampedValue = value;
if (value.CompareTo(_min) < 0) clampedValue = _min;
if (value.CompareTo(_max) > 0) clampedValue = _max;
base.Value = clampedValue;
}
}
}
// Usage
var playerHealth = new ClampedObservableProperty<int>(100, 0, 100);
playerHealth.Value = 150; // Will be clamped to 100
Reactive Chains
public class PlayerExperience : ObservableObject
{
private int _experience;
private int _level;
public int Experience
{
get => _experience;
set
{
if (Set(ref _experience, value))
{
UpdateLevel(); // Automatically update level when XP changes
}
}
}
public int Level
{
get => _level;
private set => Set(ref _level, value);
}
private void UpdateLevel()
{
Level = Mathf.FloorToInt(_experience / 1000f) + 1;
}
}
🛠️ Installation
Unity Package Manager
Add the following to your manifest.json:
{
"dependencies": {
"com.codejunkie.observables": "https://github.com/yourrepo/CodeJunkie.Observables.git"
}
}
Manual Installation
- Download the source files
- Copy to your Unity project's
Assets/Scripts/CodeJunkie/Observables/folder - Add the namespace:
using CodeJunkie.Observables;
📋 Platform Support
- Unity 2019.4+ (NET_2_0, NET_2_0_SUBSET, NET_4_6, NET_STANDARD_2_0)
- .NET Framework 4.6+
- .NET Core 2.0+
- .NET 5/6/7/8
🔄 Thread Safety
All observable collections and properties are thread-safe:
// Safe to call from multiple threads
var sharedList = new ObservableList<int>();
Task.Run(() => sharedList.Add(1));
Task.Run(() => sharedList.Add(2));
Task.Run(() => sharedList.Clear());
📚 API Reference
ObservableProperty<T>
T Value- Gets or sets the property valueevent EventHandler ValueChanged- Raised when value changesimplicit operator T- Implicit conversion to Timplicit operator ObservableProperty<T>- Implicit conversion from T
ObservableList<T>
void Add(T item)- Adds an itemvoid AddRange(IEnumerable<T> items)- Adds multiple itemsbool Remove(T item)- Removes an itemvoid RemoveRange(int index, int count)- Removes multiple itemsvoid Move(int oldIndex, int newIndex)- Moves an itemevent NotifyCollectionChangedEventHandler CollectionChanged
ObservableDictionary<TKey, TValue>
void Add(TKey key, TValue value)- Adds a key-value pairbool Remove(TKey key)- Removes a key-value pairvoid AddRange(IDictionary<TKey, TValue> items)- Adds multiple pairsevent NotifyCollectionChangedEventHandler CollectionChanged
ObservableObject
bool Set<T>(ref T field, T newValue, [CallerMemberName] string propertyName = null)void RaisePropertyChanged([CallerMemberName] string propertyName = null)event PropertyChangedEventHandler PropertyChanged
🤝 Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🙏 Acknowledgments
- Inspired by Microsoft's ObservableCollection and INotifyPropertyChanged patterns
- Optimized for Unity game development workflows
- Community feedback and contributions
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.1 is compatible. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.1
- CodeJunkie.Log (>= 1.0.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on CodeJunkie.Observables:
| Package | Downloads |
|---|---|
|
CodeJunkie.Repositories
"CodeJunkie.Repositories is a library for managing data repositories, providing a consistent interface for data access and manipulation in .NET applications." |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 212 | 6/7/2025 |