HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Designing classes for non-standard arithmetics

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
nondesigningstandardforclassesarithmetics

Problem

This question is inspired by http://anydice.com - a dice probability calculator web application.

Anydice language has three run-time types: a number, a sequence and a die. There is also a number of unary and binary operations defined on these types. Each binary operation can take any type pair (out of the 3) as arguments, there are separate definitions of what each operation does for each possible pair of types. Some operations are not commutative.

For simplicity let's consider a single binary operation, which I'll call OpAccess, or @. My goal is to design a set of classes that represent the run-time values, so that if the access operation is executed on two of such values the correct operation implementation (depending on argument types) is called.

The main problem is that at compile time it is not known yet what run-time type a value has, yet, it's required to dispatch the correct operation implementation during the run-time.

Let's start with defining the base class for our values:

abstract class Primitive 
{ 
  public abstract Primitive OpAccess(Primitive right);
}


Our value will use itself as the left argument in the OpAccessoperation and accept the right argument as the parameter.

Having this base we can design our value classes as follows:

```
class Number : Primitive
{
public override Primitive OpAccess(Primitive right)
{
return OpAccess((dynamic)right);
}
public Primitive OpAccess(Number right)
{
Console.WriteLine("Number @ Number");
return null;
}
public Primitive OpAccess(Sequence right)
{
Console.WriteLine("Number @ Sequence");
return null;
}
public Primitive OpAccess(Die right)
{
Console.WriteLine("Number @ Die");
return null;
}
}
class Sequence : Primitive
{
public override Primitive OpAccess(Primitive right)
{
return OpAccess((dynamic)right);
}
public Primitive OpAccess(Number right)
{
Console.WriteLine("Sequence @ Number");
return null;
}
public Primiti

Solution

You might want to define the base type like this

abstract class Primitive
{
    public Primitive OpAccess(Primitive right)
    {
        switch (right)
        {
            case Number number: return OpAccess(number);
            // ... other types
            default: throw new ArgumentOutOfRangeException();
        }
    }
    protected abstract Primitive OpAccess(Number right);
    // ... other OpAccess
}


where there is only one public method and the new C# 7 switch takes care of the dispatch and derived classes need to implement only the concrete protected overloads:

class Sequence : Primitive
{
    protected override Primitive OpAccess(Number right)
    {
        Console.WriteLine("Sequence @ Number");
        return null;
    }
    // ... other OpAccess
}


dynamic is no longer necessary.

Code Snippets

abstract class Primitive
{
    public Primitive OpAccess(Primitive right)
    {
        switch (right)
        {
            case Number number: return OpAccess(number);
            // ... other types
            default: throw new ArgumentOutOfRangeException();
        }
    }
    protected abstract Primitive OpAccess(Number right);
    // ... other OpAccess
}
class Sequence : Primitive
{
    protected override Primitive OpAccess(Number right)
    {
        Console.WriteLine("Sequence @ Number");
        return null;
    }
    // ... other OpAccess
}

Context

StackExchange Code Review Q#162815, answer score: 3

Revisions (0)

No revisions yet.