AufzÀhlen und wechseln, und was ist los mit ihnen

Bild



Wie oft hatten Sie eine Situation, in der Sie einer AufzĂ€hlung einen neuen Wert hinzugefĂŒgt und dann stundenlang versucht haben, alle Stellen zu finden, an denen sie verwendet wird, und dann einen neuen Fall hinzugefĂŒgt haben, damit Sie zur Laufzeit keine ArgumentOutOfRangeException erhalten?



Idee



Wenn das einzige Problem die switch-Anweisung ist und neue Typen im Auge behÀlt, lassen Sie uns sie loswerden!



Die Idee ist, die Verwendung des Schalters durch das Besuchermuster zu ersetzen.



Beispiel 1



- API , , , .



DocumentType.cs:



public enum DocumentType
{
    Invoice,
    PrepaymentAccount
}

public interface IDocumentVisitor<out T>
{
    T VisitInvoice();
    T VisitPrepaymentAccount();
}

public static class DocumentTypeExt
{
    public static T Accept<T>(this DocumentType self, IDocumentVisitor<T> visitor)
    {
        switch (self)
        {
            case DocumentType.Invoice:
                return visitor.VisitInvoice();
            case DocumentType.PrepaymentAccount:
                return visitor.VisitPrepaymentAccount();
            default:
                throw new ArgumentOutOfRangeException(nameof(self), self, null);
        }
    }
}


, , .Net . .



visitor DatabaseSearchVisitor.cs:



public class DatabaseSearchVisitor : IDocumentVisitor<IDocument>
{
    private ApiId _id;
    private Database _db;

    public DatabaseSearchVisitor(ApiId id, Database db)
    {
        _id = id;
        _db = db;
    }

    public IDocument VisitInvoice() => _db.SearchInvoice(_id);
    public IDocument VisitPrepaymentAccount() => _db.SearchPrepaymentAccount(_id);
}


:



public void UpdateStatus(ApiDoc doc)
{
    var searchVisitor = new DatabaseSearchVisitor(doc.Id, _db);

    var databaseDocument = doc.Type.Accept(searchVisitor);

    databaseDocument.Status = doc.Status;

    _db.SaveChanges();
}


2



, :



public enum PurseEventType
{
    Increase,
    Decrease,
    Block,
    Unlock
}

public sealed class PurseEvent
{
    public PurseEventType Type { get; }
    public string Json { get; }

    public PurseEvent(PurseEventType type, string json)
    {
        Type = type;
        Json = json;
    }
}


. visitor:



public interface IPurseEventTypeVisitor<out T>
{
    T VisitIncrease();
    T VisitDecrease();
    T VisitBlock();
    T VisitUnlock();
}

public sealed class PurseEventTypeNotificationVisitor : IPurseEventTypeVisitor<Missing>
{
    private readonly INotificationManager _notificationManager;
    private readonly PurseEventParser _eventParser;
    private readonly PurseEvent _event;

    public PurseEventTypeNotificationVisitor(PurseEvent @event, PurseEventParser eventParser, INotificationManager notificationManager)
    {
        _notificationManager = notificationManager;
        _event = @event;
        _eventParser = eventParser;
    }

    public Missing VisitIncrease() => Missing.Value;

    public Missing VisitDecrease() => Missing.Value;

    public Missing VisitBlock()
    {
        var blockEvent = _eventParser.ParseBlock(_event);
        _notificationManager.NotifyBlockPurseEvent(blockEvent);
        return Missing.Value;
    }

    public Missing VisitUnlock()
    {
        var blockEvent = _eventParser.ParseUnlock(_event);
        _notificationManager.NotifyUnlockPurseEvent(blockEvent);
        return Missing.Value;
    }
}


. Missing System.Reflection Unit. Result, , , .



:



public void SendNotification(PurseEvent @event)
{
    var notificationVisitor = new PurseEventTypeNotificationVisitor(@event, _eventParser, _notificationManager);
    @event.Type.Accept(notificationVisitor);
}






, , visitor . .



:



public static T Accept<TVisitor, T>(this DocumentType self, in TVisitor visitor)
    where TVisitor : IDocumentVisitor<T>
    {
        switch (self)
        {
            case DocumentType.Invoice:
                return visitor.VisitInvoice();
            case DocumentType.PrepaymentAccount:
                return visitor.VisitPrepaymentAccount();
            default:
                throw new ArgumentOutOfRangeException(nameof(self), self, null);
        }
    }


visitor , class struct.



, :



public void UpdateStatus(ApiDoc doc)
{
    var searchVisitor = new DatabaseSearchVisitor(doc.Id, _db);

    var databaseDocument = doc.Type.Accept<DatabaseSearchVisitor, IDocument>(searchVisitor);

    databaseDocument.Status = doc.Status;

    _db.SaveChanges();
}


generic, , .



in-place



, visitor — . match.



:



public static T Match<T>(this DocumentType self, Func<T> invoiceCase, Func<T> prepaymentAccountCase)
{
    var visitor = new FuncVisitor<T>(invoiceCase, prepaymentCase);
    return self.Accept<FuncVisitor<T>, T>(visitor);
}


FuncVisitor:



public readonly struct FuncVisitor<T> : IDocumentVisitor<T>
{
    private readonly Func<T> _invoiceCase;
    private readonly Func<T> _prepaymentAccountCase;

    public FuncVisitor(Func<T> invoiceCase, Func<T> prepaymentAccountCase)
    {
        _invoiceCase = invoiceCase;
        _prepaymentAccountCase = prepaymentAccountCase;
    }

    public T VisitInvoice() => _invoiceCase();
    public T VisitPrepaymentAccount() => _prepaymentAccountCase();
}


match:



public void UpdateStatus(ApiDoc doc)
{
    var databaseDocument = doc.Type.Match(
        () => _db.SearchInvoice(doc.Id),
        () => _db.SearchPrepaymentAccount(doc.Id)
    );

    databaseDocument.Status = doc.Status;

    _db.SaveChanges();
}




enum :



  1. .
  2. .


, .

case switch.



, enum.








All Articles