I'm writing a simple text adventure, and would like to have a main menu, which can be accessed at any time, while any other method is running just by typing "menu" in the console
Here's some code that I wrote (sorry, if it's not very good, I'm only starting to learn), but in this example the menu can be accessed only once at the beginning, while I'd like it to run anytime i type "menu", no matter which part of the program is currently running
So, is there any simple and efficient way to check for certain input ("menu" in this case) without copy pasting menu call method everywhere?
Any help would be appreciated, thank you in advance
(This is my previous attempt, which doesn't work, my new approach is in UPDATE1)
class Program
{
static int menuSwitch = 0;
static void Main(string[] args)
{
Console.WriteLine("Welcome to the test program");
string menu = "menu";
string trymenu = Console.ReadLine();
do
{
Console.WriteLine("Main menu");
Console.WriteLine("1.Entry1 \n2.Entry2");
Int32.TryParse(Console.ReadLine(), out menuSwitch);
switch (menuSwitch)
{
case 1:
Console.WriteLine("Entry1");
break;
case 2:
Console.WriteLine("Entry2");
break;
default:
Console.WriteLine("Exiting menu");
break;
}
break;
} while (trymenu == menu);
{
Start();
Continue();
End();
}
}
}
UPDATE1
So, I've done some tinkering and managed to get it working as intended, keeping in mind Alejandro's advice, however as I expected, now I have to call the Menu method after every step (console messages in this case) of another (Start) method
I've been wondering if there is any workaround for this, or any other efficient method to check if there is a "menu" input, as it seems kind of excessive and tedious to work with the way it is now
class Program
{
static void Main(string[] args)
{
Start();
}
static void Menu()
{
int menuSwitch = 0;
string menu = "menu";
string trymenu = Console.ReadLine();
if (trymenu == menu)
{
Console.WriteLine("Main menu");
Console.WriteLine("1.Entry1 \n2.Entry2");
Int32.TryParse(Console.ReadLine(), out menuSwitch);
switch (menuSwitch)
{
case 1:
Console.WriteLine("Entry1");
break;
case 2:
Console.WriteLine("Entry2");
break;
default:
Console.WriteLine("Exiting menu");
break;
}
}
}
static void Start()
{
Console.WriteLine("Welcome to the test program");
Menu();
Console.WriteLine("Type 'menu' to access the main menu");
Menu();
Console.WriteLine("Message1");
Menu();
Console.WriteLine("Message2");
Menu();
Console.WriteLine("Message3");
Menu();
}
}
The simplest way would probably be to move the common code to a method. You might however want to model your game as a state machine.
This allows you to separate most of the content from the game logic. This is very useful since it allows you to store the content in some file that can be loaded by the game engine. A very simple state could look something like this:
public interface IState
{
string Description { get; }
IEnumerable<ITransition> Transitions { get; }
void OnActivated(IState from);
}
public interface ITransition
{
string Command { get; }
IState TargetState { get; }
}
In this model commands are modeled as transitions between states. There are several ways to have transitions that are global:
Make a base class that contains global transitions that all states should have
Add the global states explicitly when creating each state (probably with some helper method)
Built the global transitions directly into the main game loop
An example of the last alternative could be something like this:
public class Game
{
private readonly IState initialState;
private readonly IEnumerable<ITransition> globalCommands;
private readonly IState exitState;
public Game(IState initialState, IEnumerable<ITransition> globalCommands, IState exitState)
{
this.initialState = initialState;
this.globalCommands = globalCommands;
this.exitState = exitState;
}
private IEnumerable<ITransition> GetTransitions(IState state) => state.Transitions.Concat(globalCommands);
public void Loop()
{
var currentState = initialState;
while (currentState != exitState)
{
Console.WriteLine(currentState.Description);
var transitions = GetTransitions(currentState).ToList();
foreach (var transition in transitions)
{
Console.WriteLine(transition.Command);
}
ITransition nextTransition;
do
{
var command = Console.ReadLine();
nextTransition = transitions.FirstOrDefault(t => t.Command.Equals(command));
} while (nextTransition == null);
nextTransition.TargetState.OnActivated(currentState);
currentState = nextTransition.TargetState;
}
}
}
One complexity with this is that you probably want to return to whatever state you left when you exit the menu. One way to do this could be to save the state you entered the menu from in a "exit" transition:
public class MenuState : IState
{
public string Description { get; }
IEnumerable<ITransition> IState.Transitions => Transitions.Concat(new[] {exit});
public List<ITransition> Transitions { get; } = new List<ITransition>();
private Transition exit;
public virtual void OnActivated(IState from) => exit = new Transition("exit", from);
public MenuState(string description) => Description = description;
}
Related
I'm currently trying to understand events in C # by writing my own custom event. My goal is to trigger an event after the user enters something into the console. if the string equals to "--start" something should happen. I am currently not reaching my breakpoint in the constructor of my custom event. I hope you can help me.
here is my code:
class Program
{
static void Main(string[] args)
{
Console.WriteLine($"Welcome to the BlackJack Console Game!{Environment.NewLine}" );
Console.WriteLine($"You get 500 Credits. The minimal Bet is 10 and the maximal 50!{Environment.NewLine}");
Console.WriteLine($"You can check your Creditcount with --credits{Environment.NewLine}");
Console.WriteLine($"To Start the Game write --start in the command line{Environment.NewLine}");
string userInput = Console.ReadLine();
Game game = new Game();
game.UserInput = userInput;
}
}
public class Game
{
public event EventHandler<UserInputEvent> UserWritedInput;
private string _userInput;
public string UserInput
{
get { return _userInput; }
set
{
_userInput = value;
OnUserWritedInput();
}
}
public void OnUserWritedInput()
{
UserWritedInput?.Invoke(this, new UserInputEvent(_userInput));
}
}
public class UserInputEvent : EventArgs
{
private string _userInput;
public UserInputEvent(string userInput)
{
this._userInput = userInput;
if (_userInput.Equals("--start"))
{
Console.WriteLine("game started!");
}
}
}
You haven't subscribed to the event:
class Program
{
static void Main(string[] args)
{
Console.WriteLine($"Welcome to the BlackJack Console Game!{Environment.NewLine}" );
Console.WriteLine($"You get 500 Credits. The minimal Bet is 10 and the maximal 50!{Environment.NewLine}");
Console.WriteLine($"You can check your Creditcount with --credits{Environment.NewLine}");
Console.WriteLine($"To Start the Game write --start in the command line{Environment.NewLine}");
string userInput = Console.ReadLine();
Game game = new Game();
game.UserWritedInput += OnUserWritedInput;
game.UserInput = userInput;
}
private void OnUserWritedInput(object sender, UserInputEvent args)
{
if (args.UserInput.Equals("--start"))
{
Console.WriteLine("game started!");
}
}
}
public class UserInputEvent : EventArgs
{
public string UserInput {get;}
public UserInputEvent(string userInput)
{
UserInput = userInput;
}
}
I am working on a cardgame conform the MVVM pattern. My model contains the players, hands and cards as well as the game with its rules.
There are 2 classes not playing nice here: the "card" class that has a "submitted" event: when a player clicks on an image of the card, among other things the submitted event fires. This triggers the UI to move the card from a hand to the center of the window.
Next I have a class "trick", that all players add a card to. When the trick is full, it fires the TrickFull event: this triggers the UI to show the cards in the trick and then clear the table.
During gameplay the TrickFull event fires nanoseconds after the last card was submitted. This means the table is cleared before the 4th card can be shown. I would like to be able to force the UI to process the cardsubmitted event before the Trickfull event.
I have tried to accomplish this by Thread.Sleep (which does not work), I have also tried to move the TrickFull event to the gameclass (meaning it gets triggered much later). This works, but it does seem very out of place. I have looked into locking the events (but that does not seem to be the way to go), directly taking to control of the Dispatcher, changing the priority, or maybe calling the events asynchonously and blocking the stuff somehow in the EndInvoke.
I would like to know what the best solution for this would be. My research suggests that maybe Events would not be the best pattern for this behaviour, but I am stumped. Can you bright people please advise me on how to fix this (probably architectural) flaw?
Code below, beware: Dutch classnames and stuff in there
Card (=Kaart)
public class Kaart : IComparable<Kaart>
{
public readonly Kleur Kleur;
public readonly Waarde Waarde;
public Kaart(Kleur kleur, Waarde waarde)
{
Kleur = kleur;
Waarde = waarde;
}
public event KaartGespeeld Opgegooid;
public delegate void KaartGespeeld(Kaart kaart);
public void Opgooien()
{
Opgegooid?.Invoke(this);
}
public int CompareTo(Kaart other)
{
var comparer = new KlaverjasComparer(null, null);
return comparer.Compare(this, other);
}
public Speler Speler { get; set; }
}
Trick (=Slag)
public class Slag
{
private readonly List<Kaart> _kaarten;
[Browsable(false)]
public IReadOnlyList<Kaart> Kaarten => _kaarten;
public Slag(Kleur troef)
{
_kaarten = new List<Kaart>(4);
Troef = troef;
}
public Speler Winnaar { get; private set; }
public int Punten => PuntenTeller.Punten(this);
public int Roem => PuntenTeller.Roem(this);
[Browsable(false)]
public Kleur Troef { get; }
public Kleur GevraagdeKleur { get; set; }
[Browsable(false)]
public bool Vol =>_kaarten.Count == 4;
public void Add(Kaart kaart)
{
if (!Vol)
{
if (_kaarten.Count == 0)
{
GevraagdeKleur = kaart.Kleur;
}
_kaarten.Add(kaart);
}
else
{
throw new Exception("Te veel kaarten in een slag");
}
if (!Vol) return;
Winnaar = bepaalHoogsteKaart(this).Speler;
VolleSlag?.Invoke(this);
}
public event SlagIsVol VolleSlag;
public delegate void SlagIsVol(Slag slag);
}
ViewModel:
public TafelViewModel(Boompje boompje)
{
Speler1 = boompje.Deelnemers[0];
Speler2 = boompje.Deelnemers[1];
Speler3 = boompje.Deelnemers[2];
Speler4 = boompje.Deelnemers[3];
Troef = boompje.Potje.Troef;
//boompje.SlagIsVol += Boompje_SlagIsVol;
// ToDo: als ik naar dit event kijk gaat het mis
boompje.Potje.Slag.VolleSlag += Boompje_SlagIsVol;
boompje.Potje.TroefGedraaid += delegate { Troef = boompje.Potje.Troef; };
foreach (Speler _deelnemer in boompje.Deelnemers)
{
foreach (Kaart _kaart in _deelnemer.Hand)
{
_kaart.Opgegooid += moveKaart;
}
_deelnemer.DoeIkHet += DeelnemerOnDoeIkHet;
}
_spelerKaart = new Dictionary<Speler, string>
{
{Speler1, "Kaart1"},
{Speler2, "Kaart2"},
{Speler3, "Kaart3"},
{Speler4, "Kaart4"}
};
_spelerRichting = Dictionary.SpelersRichting(boompje.Deelnemers);
WinnaarVisible = Visibility.Hidden;
}
private void Boompje_SlagIsVol(Slag slag)
{
WinnaarVisible = Visibility.Visible;
Richting = _spelerRichting[slag.Winnaar];
Application.DoEvents();
Thread.Sleep(2000);
Kaart1 = null;
Kaart2 = null;
Kaart3 = null;
Kaart4 = null;
WinnaarVisible = Visibility.Hidden;
}
private void moveKaart(Kaart kaart)
{
PropertyInfo prop = GetType().GetProperty(_spelerKaart[kaart.Speler]);
prop?.SetValue(this, kaart);
}
public void OpKaartGeklikt(Kaart kaart)
{
if (kaart.Speler != Speler3)
{
return;
}
Speler3.SpeelKaart(kaart);
}
}
}
Set ManualResetEvent in your ViewModel
ManualResetEvent manualResetEvent = new ManualResetEvent(false);
Pass this object into Kaart of yours
ManualResetEvent _manualResetEvent;
public Kaart(Kleur kleur, Waarde waarde, ManualResetEvent manualResetEvent)
{
Kleur = kleur;
Waarde = waarde;
_manualResetEvent = manualResetEvent;
}
This is the method that's being invoked when card is added I assume
public void Opgooien()
{
Opgegooid?.Invoke(this);
_manualResetEvent.Set();
}
And the main part (you also need to pass ManualResetEvent to the Slag object.
public void Add(Kaart kaart)
{
if (!Vol)
{
if (_kaarten.Count == 0)
{
GevraagdeKleur = kaart.Kleur;
}
_kaarten.Add(kaart);
}
else
{
throw new Exception("Te veel kaarten in een slag");
}
if (!Vol) return;
Winnaar = bepaalHoogsteKaart(this).Speler;
var result = _manualResetEvent.WaitOne(TimeSpan.FromSeconds(5));
if(!result)
{
/* Did not receive signal in 5 seconds */
}
VolleSlag?.Invoke(this);
_manualResetEvent.Reset();
}
Just the basic concept, it might not work right away due to your code language and lack of some part of it in the example, but you should catch the idea
I'm wondering how in C# I would go about creating a method that would take a couple set values, kind of like an enum.
public static void SwitchState(enum State { On, Off, Idle }) {
}
Something like that I don't know...I don't want to have strings passed into the method because then I have to memorize what those strings are.
public enum State { On, Off, Idle }
public static void SwitchState(State state) {
// ...
}
You actually can use enums like this
public enum SpotifyAction
{
PlayPause,
Next,
Previous,
VolumeUp,
VolumeDown,
Mute,
Quit
};
public static void controlSpotify(SpotifyAction sa)
{
BringToForeground("Spotify");
switch (sa)
{
case SpotifyAction.Next:
SendKeys.SendWait("^({RIGHT})");
break;
case SpotifyAction.Previous:
SendKeys.SendWait("^({LEFT})");
break;
case SpotifyAction.VolumeUp:
SendKeys.SendWait("^({UP})");
break;
case SpotifyAction.VolumeDown:
SendKeys.SendWait("^({DOWN})");
break;
case SpotifyAction.PlayPause:
SendKeys.SendWait(" ");
break;
}
}
You just create an enum and then pass it to the method.
public enum State
{
On,
Off,
Idle
}
private void SwitchState(State stateValue)
{
}
private void testCall()
{
SwitchState(State.Idle);
}
void Main()
{
SwitchState(State.On | State.Off);
}
public void SwitchState(State s)
{
//...
}
public enum State
{
None = 0,
On = 1,
Off = 2,
Idle = 4
}
I need to write a console emulator in C#. I want to replace System.Console with something that can work in full-screen. My approach is to make a maximized Form with no borders.
One challenge I am facing is how to convert the event-driven, non-blocking keyboard input into a blocking one, or more specifically, how would you go about implementing an equivalent of Console.ReadKey() in a Windows Form.
I don't think that you really want to have fully blocking console window. What you need is more like PowerShell or standard console window. I mean that it is doubtful that you will be against having the ability to move the window, copy data from the console and so on.
So, the console described further is based upon TextBox. Why not on the Form itself? - Because multiline, readonly TextBox already provide a lot of facilities that will simplify our Console (AppendText, text selection, copying ...).
1. Idea and interfaces:
As you have already mentioned in the question, WinForms have completely different model from console application with message queue and event based processing. So, to get data from event and prevent form from becoming irresponsive we cannot block this event loop. To achieve such behaviour we will run our console-based code on different thread with Task.Run and unblock input-dependent "console" calls from events in main form thread. The cornerstones of the application will be the next two interfaces:
public interface IConsole
{
ConsoleKeyInfo ReadKey();
void WriteLine(String line);
}
public interface IConsoleContext
{
void Run(Action<IConsole> main);
}
First is the Console itself with methods that reflect standard console capabilities. Second will run our console related code(represented by action) on some another thread, providing some console object to this action.
Our IConsoleContext implementation will be TextBoxConsoleContext with its private nested class TextBoxConsole as IConsole
2. Main form code
Here is the code we will use in our form to demonstrate the console. Assuming that we have textBox textBox_Console:
private void Form1_Load(object sender, EventArgs e)
{
var consoleContext = new TextBoxConsoleContext(this.textBox_Console);
consoleContext.Run((console) =>
{
console.WriteLine("Welcome to the TextBox console");
console.WriteLine("Press any key:");
console.ReadKey();
ConsoleKeyInfo keyInfo = new ConsoleKeyInfo();
console.WriteLine("Press y to continue: ");
do
{
keyInfo = console.ReadKey();
if (keyInfo.KeyChar == 'y')
break;
console.WriteLine("You have entered another key, please enter y to continue:");
}
while (true);
console.WriteLine("Thank you for your cooperation.");
});
}
3. The TextBoxConsoleContext itself:
public class TextBoxConsoleContext : IConsoleContext
{
#region Nested types
private class TextBoxConsole : IConsole
{
#region Fields
private TextBoxConsoleContext parent;
#endregion
#region Constructors
public TextBoxConsole(TextBoxConsoleContext parent)
{
if (parent == null)
throw new ArgumentNullException("parent");
this.parent = parent;
}
#endregion
#region IConsole implementation
public ConsoleKeyInfo ReadKey()
{
var key = this.parent.m_Queue.Dequeue();
this.WriteLine(key.KeyChar.ToString());
return key;
}
public void WriteLine(string line)
{
Action writeLine = () =>
{
var textToAppend = String.Format("{0}{1}",
line,
Environment.NewLine);
this.parent.m_TextBox.AppendText(textToAppend);
};
this.parent.m_TextBox.Invoke(writeLine);
}
#endregion
}
#endregion
#region Fields
private TextBox m_TextBox;
private OnRequestProducerConsumerQueue<ConsoleKeyInfo> m_Queue = new OnRequestProducerConsumerQueue<ConsoleKeyInfo>();
private Boolean m_Shift;
private Boolean m_Alt;
private Boolean m_Ctrl;
private ConsoleKey m_KeyInfo;
#endregion
#region Constructors
public TextBoxConsoleContext(TextBox textBox)
{
if (textBox == null)
throw new ArgumentNullException("textBox");
this.m_TextBox = textBox;
this.m_TextBox.ReadOnly = true;
// Event handler that will read key down data before key press
this.m_TextBox.KeyDown += (obj, e) =>
{
this.m_Shift = e.Modifiers.HasFlag(Keys.Shift);
this.m_Alt = e.Modifiers.HasFlag(Keys.Alt);
this.m_Ctrl = e.Modifiers.HasFlag(Keys.Control);
if (!Enum.TryParse<ConsoleKey>(e.KeyCode.ToString(), out this.m_KeyInfo))
{
this.m_KeyInfo = ConsoleKey.Escape;
}
};
this.m_TextBox.KeyPress += (obj, e) =>
{
this.m_Queue.EnqueueIfRequired(new ConsoleKeyInfo(e.KeyChar,
this.m_KeyInfo,
this.m_Shift,
this.m_Alt,
this.m_Ctrl));
};
}
#endregion
#region IConsoleContext implementation
public void Run(Action<IConsole> main)
{
if (main == null)
throw new ArgumentNullException("main");
var console = new TextBoxConsole(this);
Task.Run(() =>
main(console));
}
#endregion
}
4. OnRequestProducerConsumerQueue
EDIT: I have replaced initial queue class with this much simpler class that uses single lock object with Monitor calls. This code is based on the condition variable pattern.
REMARK: I just wanted to replace the previous abuse of sync constructs I have posted initially. This version doesn't change anything in the behaviour, and the EnqueueIfRequired in this implementation will be more than useless if you need to implement ReadLine methods (but you can add to it some predicate logic that will allow queening till the line terminator appears).
public class OnRequestProducerConsumerQueue<T>
{
private Queue<T> m_Items = new Queue<T>();
private object m_lock = new Object();
private Int32 m_NeedItems = 0;
public void EnqueueIfRequired(T value)
{
lock (this.m_lock)
{
if (this.m_NeedItems == 0)
return;
this.m_Items.Enqueue(value);
this.m_NeedItems--;
Monitor.PulseAll(this.m_lock);
}
}
public T Dequeue()
{
lock (this.m_lock)
{
this.m_NeedItems++;
while (this.m_Items.Count < 1)
{
Monitor.Wait(this.m_lock);
}
return this.m_Items.Dequeue();
}
}
}
I've been doing some reading on the Strategy Pattern, and have a question. I have implemented a very basic Console Application below to explain what I'm asking.
I have read that having 'switch' statements is a red flag when implementing the strategy pattern. However, I can't seem to get away from having a switch statement in this example. Am I missing something? I was able to remove the logic from the Pencil, but my Main has a switch statement in it now. I understand that I could easily create a new TriangleDrawer class, and wouldn't have to open the Pencil class, which is good. However, I would need to open Main so that it would know which type of IDrawer to pass to the Pencil. Is this just what needs to be done if I'm relying on the user for input? If there's a way to do this without the switch statement, I'd love to see it!
class Program
{
public class Pencil
{
private IDraw drawer;
public Pencil(IDraw iDrawer)
{
drawer = iDrawer;
}
public void Draw()
{
drawer.Draw();
}
}
public interface IDraw
{
void Draw();
}
public class CircleDrawer : IDraw
{
public void Draw()
{
Console.Write("()\n");
}
}
public class SquareDrawer : IDraw
{
public void Draw()
{
Console.WriteLine("[]\n");
}
}
static void Main(string[] args)
{
Console.WriteLine("What would you like to draw? 1:Circle or 2:Sqaure");
int input;
if (int.TryParse(Console.ReadLine(), out input))
{
Pencil pencil = null;
switch (input)
{
case 1:
pencil = new Pencil(new CircleDrawer());
break;
case 2:
pencil = new Pencil(new SquareDrawer());
break;
default:
return;
}
pencil.Draw();
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
}
Implemented Solution shown below (Thanks to all who responded!)
This solution got me to the point where the only thing I need to do to use a new IDraw object is to create it.
public class Pencil
{
private IDraw drawer;
public Pencil(IDraw iDrawer)
{
drawer = iDrawer;
}
public void Draw()
{
drawer.Draw();
}
}
public interface IDraw
{
int ID { get; }
void Draw();
}
public class CircleDrawer : IDraw
{
public void Draw()
{
Console.Write("()\n");
}
public int ID
{
get { return 1; }
}
}
public class SquareDrawer : IDraw
{
public void Draw()
{
Console.WriteLine("[]\n");
}
public int ID
{
get { return 2; }
}
}
public static class DrawingBuilderFactor
{
private static List<IDraw> drawers = new List<IDraw>();
public static IDraw GetDrawer(int drawerId)
{
if (drawers.Count == 0)
{
drawers = Assembly.GetExecutingAssembly()
.GetTypes()
.Where(type => typeof(IDraw).IsAssignableFrom(type) && type.IsClass)
.Select(type => Activator.CreateInstance(type))
.Cast<IDraw>()
.ToList();
}
return drawers.Where(drawer => drawer.ID == drawerId).FirstOrDefault();
}
}
static void Main(string[] args)
{
int input = 1;
while (input != 0)
{
Console.WriteLine("What would you like to draw? 1:Circle or 2:Sqaure");
if (int.TryParse(Console.ReadLine(), out input))
{
Pencil pencil = null;
IDraw drawer = DrawingBuilderFactor.GetDrawer(input);
pencil = new Pencil(drawer);
pencil.Draw();
}
}
}
Strategy isn't a magic anti-switch solution. What it does do is give modularise your code so that instead of a big switch and business logic all mixed up in a maintenance nightmare
your business logic is isolated and open for extension
you have options as for how you create your concrete classes (see Factory patterns for example)
your infrastructure code (your main) can be very clean, free of both
For example - if you took the switch in your main method and created a class which accepted the command line argument and returned an instance of IDraw (i.e. it encapsulates that switch) your main is clean again and your switch is in a class whose sole purpose is to implement that choice.
I don't think your switch here in your demo app is actually part of the strategy pattern itself, it is just being used to exercise the two different strategies you have defined.
The "switches being a red flag" warning refers to having switches inside the strategy; for example, if you defined a strategy "GenericDrawer", and had it determine if the user wanted a SquareDrawer or CircleDrawer internally using a switch against a parameter value, you would not be getting the benefit of the strategy pattern.
The following is an over engineered solution to your problem solely for the sake of avoiding if/switch statements.
CircleFactory: IDrawFactory
{
string Key { get; }
IDraw Create();
}
TriangleFactory: IDrawFactory
{
string Key { get; }
IDraw Create();
}
DrawFactory
{
List<IDrawFactory> Factories { get; }
IDraw Create(string key)
{
var factory = Factories.FirstOrDefault(f=>f.Key.Equals(key));
if (factory == null)
throw new ArgumentException();
return factory.Create();
}
}
void Main()
{
DrawFactory factory = new DrawFactory();
factory.Create("circle");
}
You can also get rid of if with help of a dictionary
Dictionary<string, Func<IDraw> factory> drawFactories = new Dictionary<string, Func<IDraw> factory>() { {"circle", f=> new CircleDraw()}, {"square", f=> new SquareDraw()}}();
Func<IDraw> factory;
drawFactories.TryGetValue("circle", out factory);
IDraw draw = factory();
A little to late but for anyone that still is interested in fully removing a conditional statement.
class Program
{
Lazy<Dictionary<Enum, Func<IStrategy>>> dictionary = new Lazy<Dictionary<Enum, Func<IStrategy>>>(
() =>
new Dictionary<Enum, Func<IStrategy>>()
{
{ Enum.StrategyA, () => { return new StrategyA(); } },
{ Enum.StrategyB, () => { return new StrategyB(); } }
}
);
IStrategy _strategy;
IStrategy Client(Enum enu)
{
Func<IStrategy> _func
if (dictionary.Value.TryGetValue(enu, out _func ))
{
_strategy = _func.Invoke();
}
return _strategy ?? default(IStrategy);
}
static void Main(string[] args)
{
Program p = new Program();
var x = p.Client(Enum.StrategyB);
x.Create();
}
}
public enum Enum : int
{
StrategyA = 1,
StrategyB = 2
}
public interface IStrategy
{
void Create();
}
public class StrategyA : IStrategy
{
public void Create()
{
Console.WriteLine("A");
}
}
public class StrategyB : IStrategy
{
public void Create()
{
Console.WriteLine("B");
}
}