https://github.com/Stepami/visitor-net
  
  
    First-ever acyclic generic extensible typesafe implementation of Visitor pattern for .NET without any usage of dynamic cast 
    https://github.com/Stepami/visitor-net
  
acyclic compile-time csharp extensible generics typesafe visitor-pattern
        Last synced: 7 months ago 
        JSON representation
    
First-ever acyclic generic extensible typesafe implementation of Visitor pattern for .NET without any usage of dynamic cast
- Host: GitHub
 - URL: https://github.com/Stepami/visitor-net
 - Owner: Stepami
 - License: gpl-3.0
 - Created: 2022-07-27T14:58:15.000Z (over 3 years ago)
 - Default Branch: master
 - Last Pushed: 2023-12-12T18:32:51.000Z (almost 2 years ago)
 - Last Synced: 2024-03-26T09:02:07.349Z (over 1 year ago)
 - Topics: acyclic, compile-time, csharp, extensible, generics, typesafe, visitor-pattern
 - Language: C#
 - Homepage:
 - Size: 92.8 KB
 - Stars: 23
 - Watchers: 2
 - Forks: 0
 - Open Issues: 1
 - 
            Metadata Files:
            
- Readme: README.md
 - License: LICENSE
 
 
Awesome Lists containing this project
- RSCG_Examples - https://github.com/Stepami/visitor-net - net (Contributors Welcome for those / 1. [ThisAssembly](https://ignatandrei.github.io/RSCG_Examples/v2/docs/ThisAssembly) , in the [EnhancementProject](https://ignatandrei.github.io/RSCG_Examples/v2/docs/rscg-examples#enhancementproject) category)
 - csharp-source-generators - Visitor.NET -   First-ever acyclic generic extensible typesafe implementation of Visitor pattern for .NET without any usage of dynamic cast. Implementation powered by Source Generators (Source Generators / Patterns)
 
README
          
Status:

[](https://www.nuget.org/packages/Visitor.NET/)
# Visitor.NET
First-ever acyclic generic extensible typesafe implementation of Visitor pattern for .NET **without any usage of dynamic cast**.
With Visitor.NET you can develop typesafe acyclic visitors even if you do not have access to source code of visitable structures.
## Installation
### NuGet
Install package : [https://www.nuget.org/packages/Visitor.NET](https://www.nuget.org/packages/Visitor.NET).
### GitHub
- Clone locally this github repository
- Build the `Visitor.NET.sln` solution
## Projects using Visitor.NET
- [HydraScript](https://github.com/Stepami/hydrascript)
## Usage
### Basic Example
Let's say we have some expression-tree-like hierarchy, implementing basic arithmetics, like this:
```csharp
public abstract record BinaryTreeNode;
public record Operation(char Symbol, BinaryTreeNode Left, BinaryTreeNode Right) : BinaryTreeNode;
public record Number(double Value) : BinaryTreeNode;
public record Parenthesis(BinaryTreeNode Node) : BinaryTreeNode;
```
So we may want to traverse it in order to, for example, compute expression result.
First of all, we implement evaluator using [`IVisitor<,>`](Visitor.NET/IVisitor.cs) interface:
```csharp
public class BinaryTreeEvaluator : VisitorBase,
    IVisitor,
    IVisitor,
    IVisitor
{
    public double Visit(Operation visitable) =>
        visitable.Symbol switch
        {
            '+' => visitable.Left.Accept(This) + visitable.Right.Accept(This),
            _ => throw new NotImplementedException()
        };
    public double Visit(Number visitable) => visitable.Value;
    public double Visit(Parenthesis visitable) => visitable.Node.Accept(This);
}
```
But then we have to tell structures we visit that they are visitable.
It is done through [`IVisitable<>`](Visitor.NET/IVisitable.cs) interface implementation:
```csharp
public abstract record BinaryTreeNode : IVisitable
{
    public abstract TReturn Accept(
        IVisitor visitor);
}
public record Operation(
    char Symbol,
    BinaryTreeNode Left,
    BinaryTreeNode Right) : BinaryTreeNode, IVisitable
{
    public override TReturn Accept(
        IVisitor visitor) =>
        Accept(visitor);
    public TReturn Accept(
        IVisitor visitor) =>
        visitor.Visit(this);
}
public record Number(double Value) : BinaryTreeNode, IVisitable
{
    public override TReturn Accept(
        IVisitor visitor) =>
        Accept(visitor);
    public TReturn Accept(
        IVisitor visitor) =>
        visitor.Visit(this);
}
public record Parenthesis(BinaryTreeNode Node) : BinaryTreeNode, IVisitable
{
    public override TReturn Accept(
        IVisitor visitor) =>
        Accept(visitor);
    public TReturn Accept(
        IVisitor visitor) =>
        visitor.Visit(this);
}
```
Basically, if you have access to source code of structure you want "visit", it's better to always have implementation:
```csharp
return visitor.Visit(this);
```
In case you would make `Visit` implementation procedure (i.e. have no returning value), use [`VisitUnit` type](https://en.wikipedia.org/wiki/Unit_type) as return type.
So, your method would look like this:
```csharp
public class SomeVisitor : IVisitor
{
    public VisitUnit Visit(Some visitable)
    {
        //...
        return default;
    }
}
```
### Adapter Usage
Let's imagine you want to visit some structure defined outside of your project (library, dto, etc.):
```csharp
public record LinkedListNode(T Data, LinkedListNode Next)
{
    public bool HasNext() => Next != null;
}
```
So we may define wrapper around instance of this type which would [became visitable](Visitor.NET/Adapter/VisitableAdapter.cs):
```csharp
public class LinkedListToVisitableAdapter :
    VisitableAdapter>,
    IVisitable>
{
    public LinkedListToVisitableAdapter(LinkedListNode data) :
        base(data)
    {
    }
    public override TReturn Accept(
        IVisitor>, TReturn> visitor) =>
        Accept(visitor);
    public TReturn Accept(
        IVisitor, TReturn> visitor) =>
        visitor.Visit(this);
}
```
This adapter can be instantiated with [`VisitableAdapterFactory<>`](Visitor.NET/Adapter/VisitableAdapterFactory.cs) implementation:
```csharp
public class LinkedListToVisitableAdapterFactory :
    VisitableAdapterFactory>
{
    public override LinkedListToVisitableAdapter Create(LinkedListNode data) =>
        new(data);
}
```
Bringing it all together:
```csharp
public class LinkedListNodePrinter : VisitorNoReturnBase>>,
    IVisitor>
{
    private readonly StringBuilder _sb = new();
    private readonly VisitableAdapterFactory> _factory;
    public LinkedListNodePrinter(VisitableAdapterFactory> factory) =>
        _factory = factory;
    public VisitUnit Visit(LinkedListToVisitableAdapter visitable)
    {
        var node = visitable.Data;
        _sb.Append(node.Data);
        if (node.HasNext())
        {
            var next = _factory.Create(node.Next);
            _sb.Append("->");
            next.Accept(This);
        }
        return default;
    }
    public override string ToString() => _sb.ToString();
}
```
# Visitor.NET.AutoVisitableGen
If you do not want implement visitable manually, you can do it automatically with incremental source generator.
Install package : [https://www.nuget.org/packages/Visitor.NET.AutoVisitableGen](https://www.nuget.org/packages/Visitor.NET.AutoVisitableGen).
Then, rewrite the nodes type declarations like this:
```csharp
public abstract record BinaryTreeNode : IVisitable
{
    public abstract TReturn Accept(
        IVisitor visitor);
}
[AutoVisitable]
public partial record Operation(
    char Symbol,
    BinaryTreeNode Left,
    BinaryTreeNode Right) : BinaryTreeNode;
[AutoVisitable]
public partial record Number(double Value) : BinaryTreeNode;
[AutoVisitable]
public partial record Parenthesis(BinaryTreeNode Node) : BinaryTreeNode;
```