patterncsharpMinor
Designing classes for non-standard arithmetics
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
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:
Our value will use itself as the left argument in the
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
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
where there is only one
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.