I want to implement the typestate pattern in C#. I've tried something like:
interface Open {}
interface Closed {}
abstract class Foo<T> { }
class Bar<T> where T: Closed { }
class Bar<T> where T: Open { }
as well as other permutations, but they all end up with the compiler complaining. This particular example gives (via https://www.programiz.com/csharp-programming/online-compiler/):
/tmp/qmk1tTLHWG.cs(12,11): error CS0101: The namespace 'baz' already contains a definition for 'Bar'
/tmp/qmk1tTLHWG.cs(11,11): error CS0265: Partial declarations of 'Bar<T>' have inconsistent constraints for type parameter 'T'
I don't know enough about C#'s type system to know what will actually work, and I can't find much documentation about how to do this. Is it possible to implement the type state pattern? I found Adam's blog however he doesn't actually implement the type state pattern as shown in 1, which is what I'd like to do if possible.
Ideally I'd like something like
struct Closed { };
struct Open { };
class Foo<T> {
// functions applicable to Foo's in both the Closed and Open states
}
class Foo<Closed> {
// functions applicable to Foo's in just the Open state
}
class Foo<Open> {
// functions applicable to Foo's in just the Open state
}
You should model this via interfaces, but there only needs to be a single class.
An example:
interface IOpen
{
IClosed Close();
}
interface IClosed
{
IOpen Open();
}
class Foo : IOpen, IClosed
{
public IOpen Open()
{
return this;
}
public IClosed Close()
{
return this;
}
}
var foo = new Foo();
var open = foo.Open();
var closed = open.Close(); // Only Close() is allowed on an open foo
closed.Open(); // Only Open() is allowed on an closed foo
You can extend this by forcing a default state on Foo
using a factory method:
class Foo : IOpen, IClosed
{
private Foo() // Prevent manual instantiation (using new)
{ }
public static IClosed Create() // Return closed by default
{
return new Foo();
}
public IOpen Open()
{
return this;
}
public IClosed Close()
{
return this;
}
}
var closed = Foo.Create();
var open = closed.Open(); // Only Open() is allowed by default