/MixinGenerator

A Code-Aware library for giving additional functionality by composition-over-inheritance, a mixin-like approach.

Primary LanguageC#MIT LicenseMIT

MixinGenerator

How to use

  1. Composition

Composition

  1. Quick Action (C# Analyzer & Code Fix))

Code Fix

  1. Delegation (Generated Source Code)

Delegation

Why composition over inheritance

mixins VS base classes

A class can have multiple mixins.

// base class
// ❌ multiple inheritance (compilation error)
class Inheritance : BindableBase, DisposableBase
{
}

// mixin
// ⭕ multiple mixins
class Composition
{
    // MixinGenerator/MixinGenerator.Mixins/Mixins/NotifyPropertyChanged.cs
    NotifyPropertyChanged _npc;

    // MixinGenerator/MixinGenerator.Mixins/Mixins/DisposableList.cs
    DisposableList _d;
}

class BindableBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

class DisposableBase : IDisposable
{
    private List<IDisposable> _disposables = new List<IDisposable>();

    public void Dispose()
    {
        foreach (var d in _disposables) d?.Dispose();
        _disposables.Clear();
    }
}

mixins VS interface default methods

Mixins can have state.

// interface
// (C# 8.0 allows interface to have default method implementation)
interface INotifyPropertyChanged
{
    //❌ This is an abstract declaration. There is no backing field in the interface
    event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

interface ICompositeDisposable : IDisposable
{
    //❌ An intarface can't have fields
    private List<IDisposable> _disposables = new List<IDisposable>();

    void Dispose()
    {
        foreach (var d in _disposables) d?.Dispose();
        _disposables.Clear();
    }
}

// mixin
// ⭕ state (fields)
[NonCopyable]
[Mixin]
public struct NotifyPropertyChanged : INotifyPropertyChanged
{
    // has a backing field for the event
    public event PropertyChangedEventHandler PropertyChanged;

    [Accessibility(Accessibility.Protected)]
    public void OnPropertyChanged([This] object @this, PropertyChangedEventArgs args) => PropertyChanged?.Invoke(@this, args);
}

[NonCopyable]
[Mixin]
public struct DisposableList : IDisposable
{
    // has a field
    private List<IDisposable> _list;

    public void Dispose()
    {
        var list = _list;
        _list = null;
        foreach (var d in list) d.Dispose();
    }
}