2015-06-13 13 views
8

Ich versuche, in C# Generika zu bekommen und habe eine Zustandsmaschine mit dem Zustandsmuster erstellt und jetzt versuche ich zu refactor.C# Generika, Querverweis Klassen für Zustandsmuster

Ich habe einen Zustand, der einen Verweis auf das Objekt hat, an dem es arbeitet.

und ich habe das Objekt, das Staaten hat, sollte dies einen Verweis auf seinen aktuellen Zustand haben.

public abstract class StatefulObject<T> : MonoBehaviour where T : AbstractState<StatefulObject<T>> { 

    public T state; 

} 

Aber es funktioniert nicht („der Typ kann nicht als Typ-Parameter‚t‘verwendet wird, in dem generischen Typ oder Methode“).

Was ich will, ist, so etwas erreichen:

public class Monster : StatefulObject<MonsterState> { 

} 

public abstract class MonsterState : AbstractState<Monster> { 

} 

Ist das möglich? Wenn es nicht so ist, gibt es einen anderen? Danke.

+0

Wie deklarieren Sie 'StatefulObject' als generischen Typ (2.Snippet) und verwenden es als nicht-generischen Typ (1.Snippet)? –

Antwort

3

Sie können Missbrauch Schnittstellen und Varianz, das erreichen:

public interface IState<out TObject, in TState> 
    where TObject : IStatefulObject<TObject, TState> 
    where TState : IState<TObject, TState> 
{ 
} 

public interface IStatefulObject<in TObject, out TState> 
    where TObject : IStatefulObject<TObject, TState> 
    where TState : IState<TObject, TState> 
{ 
} 

public abstract class AbstractState<TObject> : IState<TObject, AbstractState<TObject>> 
    where TObject : IStatefulObject<TObject, AbstractState<TObject>> 
{ 
    protected TObject Object { get; private set; } 

    public AbstractState(TObject obj) 
    { 
     Object = obj; 
    } 
} 

public abstract class StatefulObject<TState> : IStatefulObject<StatefulObject<TState>, TState> 
    where TState : IState<StatefulObject<TState>, TState> 
{ 
    protected TState State { get; set; } 
} 

public class Monster : StatefulObject<MonsterState> 
{ 
    public Monster() 
    { 
     State = new IdleMonsterState(this); 
    } 
} 

public abstract class MonsterState : AbstractState<Monster> 
{ 
    protected MonsterState(Monster monster) 
     : base(monster) 
    { 
    } 
} 

public class IdleMonsterState : MonsterState 
{ 
    public IdleMonsterState(Monster monster) 
     : base(monster) 
    { 
    } 
} 

Ob es tatsächlich eine gute Idee ist zweifelhaft, kann ein solcher Code zu verwirrend sein.


Sie auch mit dem einfacheren (aber weniger stark typisierte) Ansatz gehen könnten:

public abstract class AbstractState 
{ 
} 

public abstract class StatefulObject 
{ 
} 

public abstract class AbstractState<TObject> : AbstractState 
    where TObject : StatefulObject 
{ 
    protected TObject Object { get; private set; } 

    public AbstractState(TObject obj) 
    { 
     Object = obj; 
    } 
} 

public abstract class StatefulObject<TState> : StatefulObject 
    where TState : AbstractState 
{ 
    protected TState State { get; set; } 
} 

public class Monster : StatefulObject<MonsterState> 
{ 
} 

public abstract class MonsterState : AbstractState<Monster> 
{ 
    protected MonsterState(Monster monster) 
     : base(monster) 
    { 
    } 
} 

Dies wird nicht sicherstellen, dass Sie nicht zuordnen können, zu sagen, ein PlayerState ein Monster sollten Sie dies zur Laufzeit überprüfen.

+0

Einer der Nachteile dieses Designs ist, dass Sie einem Monster jeden Zustand zuweisen können, der nicht darauf beschränkt ist, dass der Zustand ein MonsterState ist. –

+0

@MarcCals ja, ich habe das Ziel von OP tatsächlich missverstanden. Ich schrieb die Antwort um. –

+0

@LucasTrzesniewski Es funktioniert. Ich werde etwas Zeit brauchen, um das zu durchschauen :) Zuerst beschwerte sich der Compiler über die Ein- und Ausgabe in den Schnittstellen und ich musste das Ziel auf .net4.0 setzen (ich arbeite mit Unity). Danke :) – ElDuderino