The title is a mouthful and accurately implies an alarmingly high jargon to
code ration, but I just didn't see anyway to write this post without straying
into all of these different subjects. When you try to write an explanatory
article you have to walk a tightrope between a sample problem that's simple
enough to work through in no more than a handful of pages and a sample problem
that's just too simplistic to be valuable. I'll leave it up to you to decide
which end of the spectrum this one falls on. I also had a rough time trying to
decide on the best way to order the topics in the narrative. All I can do is
ask you to scan the following headers if something seems to be missing or I'm
lurching ahead.
Last week I had a flood of people follow Martin Fowler's
link into this series. I thought it was pretty cool because most of my UI
patterns material and terminology is transparently based on Fowler's work. I'm
especially glad that Martin didn't mention the fact that I volunteered to help
with the UI patterns writing three years ago then disappeared...
If you missed something, here's the Build your own CAB series in its
entirety.
- Preamble
- The
Humble Dialog Box
- Supervising
Controller
- Passive
View
- Presentation
Model
- View
to Presenter Communication
- Answering
some questions
- What's
the Model?
- Assigning
Responsibilities in a Model View Presenter Architecture
- Domain
Centric Validation with the Notification Pattern
- Unit
Testing the UI with NUnitForms
- Event
Aggregator
- Rein
in runaway events with the "Latch"
- Embedded
Controllers with a Dash of DSL
- Managing Menu State with MicroController's, Command's, a Layer SuperType,
some StructureMap Pixie Dust, and a Dollop of Fluent Interface (This Post)
- Replacing Data Binding with MicroControllers and the Window Driver Pattern -
Forthcoming
- Subcutaneous Testing - Forthcoming
- Creating the Application Shell - probably a couple posts
- Wiring the Components with an IoC tool - Forthcoming, but I may push this
off into late summer
- A Day in the Life of a Screen
The end is in sight. In traditional developer style I blew the estimate for
how long "Build your own CAB" would take. I thought all I needed to do was copy
n'paste a bunch of verbiage and code from my DevTeach talks into Live Writer and
that would be that - but my usual wordiness kicked in and I ended up submitting
a completely new talk to DevTeach Vancouver on this subject.
A couple years ago I remember reading Stephen King say that he always found
the Dark Tower books difficult to write as a way of explaining why there was so
much lag between new books, much to my exasperation at the time. I'm obviously
not Stephen King, and there's no line of folks wrapped around the corner of
CodeBetter waiting for the next installment, but I know how Stephen King felt
now. I do promise that the end of this series won't be as
shocking/disappointing/brilliant(?) as the end of the Dark Tower.*
Problem Statement
Again, I am not a licensed namer of patterns, and some of the people I've
shown this code to have commented that it's reminiscent of Smalltalk UI's, so
it's a good bet that some of you have already seen or used similar approaches.
That said, I use the term "MicroController" to refer to a controller class that
directs the behavior of a single UI widget. By itself, a MicroController isn't
really that powerful, but like an army of ants, a group of MicroController's
working cooperatively (with some external direction) can accomplish powerful
feats.
Here's a common scenario:
- You have a menu bar of some sort or another in the top strip of your
composite WinForms application
- Each individual menu item needs to fire off a command of some sort (duh)
- The availability of some, but not all, menu items is dependent on user
roles. In other words, some menu items will be disabled or hidden if a user
does not have a necessary role.
- The availability of some menu items is dependent upon the screen that is
currently active in an application with some sort of MDI or tabbed interface
- The menu bar frequently collects new additions as the application
grows
There's nothing in that list that's particularly hard or even unusual, but I
want to explore an alternative approach. Off the top of my head, I've got four
goals in mind for the design of my menu state management:
- Quickly attach the proper commands and actions to each MenuItem. This can
get tricky because each MenuItem potentially touches and interacts with very
different pieces of the application.
- Eliminate duplicate grunge coding. I say you pursue any chance to eliminate
the mechanical cost, and there's quite a bit of repetitive functionality here.
- Make the code that specifies the menu behavior as expressive and readable as
possible. This code is going to frequently change, so it's worth our while to
make this code readable.
- I want an easy way for each screen to configure the menu state, and I want
this menu state calculation and manipulation to be as easy to understand, write,
and test as possible.
Much like the screen state machine sample from my
last post, I'm going to try to use a Domain Specific Language (DSL) / Fluent
Interface to express and define the menu behavior. By hiding the mechanics
of menu management behind an abstracted Fluent Interface I'm hoping to compress
the code that governs the menu state to a smaller area of the code. I want to
be able to understand the menu behavior by scanning a cohesive area of the
code. It's my firm contention that this type of readability simply cannot be
accomplished by using the designer to attach bits and pieces of behavior.
Leaning on the designer will scatter the behavior of the screen all over the
place. One of the main reasons I don't like to use the designer or wizards is
because you often can't "see" the code and the way it works.
Start with the End in Mind
Before zooming in one the individual components of the solution, let's keep
the man firmly behind the curtain and look at my intended end state. Inside
some screen (probably the main Form) is a piece of code that expresses the
behavior of the menu items like this fragment below:
private void configureMenus()
{ _menuController.MenuItem(openItem).Executes(CommandNames.Open).IsAlwaysEnabled();
_menuController.MenuItem(saveItem).Executes(CommandNames.Save)
.IsAvailableToRoles("BigBoss", "WorkerBee");
_menuController.MenuItem(executeItem).Executes(CommandNames.Execute)
.IsAvailableToRoles("BigBoss");
_menuController.MenuItem(exportItem).Executes(CommandNames.Export);
}
And in each individual screen presenter you might see some additional code to
set screen-specific menu settings like this that would be called upon activating
a different screen:
public class SomeScreenPresenter : IPresenter
{ public void ConfigureMenu(MenuState state)
{ state.Enable(CommandNames.Save, CommandNames.Export);
state.Enable(CommandNames.Execute).ForRoles("SpecialWorkerBee", "BigBoss"); }
}
So what's going on here? There isn't a single call in this code to
MenuItem.Enabled or any definition of MenuItem.Click, so it's safe to assume
that there's somebody behind the curtain. So, what is the man behind the
curtain? Before I talk about each piece in detail, here's a rundown of the
various moving parts:
- A small MicroController object for each MenuItem that "knows" when to enable
or disable its MenuItem and set up the MenuItem's Click event.
- A controller class for the menu that aggregates all of the MenuItem
controllers. Part of the responsibility of the Fluent Interface
configuration code above is to associate a MenuItem with a CommandNames key so
we can quickly reference the correct MenuItem's when a different screen is
activated.
- A series of Command
pattern classes that all implement a common interface.
- StructureMap is lurking in
the background as my IoC tool of choice to wire the various parts together.
- The Fluent Interface configuration API. For the most part, it's job is to
look pretty. All it's doing is gathering in input values to set on the
individual MenuItem controller classes that do the real work. What I've found
so far is that Fluent Interface strategies seem to lend themselves best to
situations like this where you're really just configuring an object graph to do
the real work.
- A MenuState object that is used to transmit the desired state of the menu
from the individual screens to the main menu controller
- Each Presenter in the application implements a common interface that
includes some sort of hook to configure the items on the main Menu.
- The whole application is stitched together with the combination of an
ApplicationController and ApplicationShell. I'm going to be very brief on these
two because I'm going to devote a couple posts later down the line to these
classes.
The complete "Build
your own CAB" Table of Contents is now up if you've missed some of the
earlier missives.
Continuing where I left off in Build
your own CAB #14: Managing Menu State with MicroController's, Command's, a Layer
SuperType, some StructureMap Pixie Dust, and a Dollop of Fluent Interface ,
I'll show how to build a Fluent Interface API to configure menu state management
in a WinForms application while using as many buzzwords as humanly possible.
Going backwards "Memento" style, the end state is shown in the first post (I had
to split the content because Community Server whined at me).
Problem Statement
- You have a menu bar of some sort or another in the top strip of your
composite WinForms application
- Each individual menu item needs to fire off a command of some sort (duh)
- The availability of some, but not all, menu items is dependent on user
roles. In other words, some menu items will be disabled or hidden if a user
does not have a necessary role.
- The availability of some menu items is dependent upon the screen that is
currently active in an application with some sort of MDI or tabbed interface
- The menu bar frequently collects new additions as the application
grows
Let's Treat all Presenters Exactly the Same with the Layer SuperType
Pattern
From PEAA,
a Layer SuperType is
A type that acts as the supertype for all types in its
layer.
In all of the WinForms applications I've worked on with Model View Presenter
the Presenter's have implemented some sort of Layer Supertype interface or base
class. The details differ quite a bit from project to project, but the pattern
seems to always be there. The methods on the common interface usually relate to
setting up screen state or to transitioning between screens. Here's a sample
IPresenter interface that's pretty typical to my projects:
public interface IPresenter
{ void ConfigureMenu(MenuState state);
void Activate();
void Deactivate();
bool CanClose();
}
The Presenter interface is mostly a set of hook methods for the ApplicationController
to call to set up or tear down a screen. While I'll revisit this topic in much
more detail later, for now let's focus in on the bolded method in the IPresenter
interface above.
Pushing Menu State Around
It's safe to assume that nearly every screen is going to have a different set
of rules for which menu items are available and valid for user rules. By
implementing a common interface across all screen Presenter's we can establish a
standard way to query a Presenter for its particular menu state. What we need
next is an easy way to transmit the screen specific business rules from each
screen to the menu. The logic and business rules to determine the menu state
really fits
into each Presenter, but we don't want the Presenter to know about the
concrete Menu because we don't want to bind our presentation logic to UI
machinery. We could wrap the Menu in some some sort of abstracted IMenu
interface that we could mock while testing the Presenter, but I think there's a
better way. By and large I think that state-based testing is generally easier
than interaction-based testing. In this case I've opted to use a class called
MenuState to configure and transfer screen state from the Presenter to the
Menu. MenuState looks something like this:
public class MenuState
{ private Dictionary<CommandNames, string[]> _enabledByRoleCommands
= new Dictionary<CommandNames, string[]>();
public void Enable(params CommandNames[] names)
{ foreach (CommandNames name in names)
{ _enabledByRoleCommands.Add(name, new string[0]);
}
}
public EnableByRoleExpression Enable(CommandNames name)
{ return new EnableByRoleExpression(name, this);
}
public class EnableByRoleExpression
{ private readonly CommandNames _names;
private readonly MenuState _state;
internal EnableByRoleExpression(CommandNames names, MenuState state)
{ _names = names;
_state = state;
}
public void ForRoles(params string[] roles)
{ _state._enabledByRoleCommands.Add(_names, roles);
}
}
public bool IsEnabled(CommandNames name)
{ return _enabledByRoleCommands.ContainsKey(name);
}
public string[] GetRolesFor(CommandNames name)
{ if (_enabledByRoleCommands.ContainsKey(name))
{ return _enabledByRoleCommands[name];
}
return null;
}
}
Much like the
Notification class from the earlier post on validation, the MenuState class
helps us to keep the coupling between the menu system and each screen to a
minimum. Also like a Notification, creating a MenuState object from the
Presenter makes it relatively easy to unit test the menu state logic in each
Presenter. We'll write interaction tests with mock objects to make sure that
the navigation and screen coordination code is correctly applying the result of
a call to IPresenter.ConfigureMenu(MenuState) method first. After that, we can
concentrate on just testing the result of a call to
IPresenter.ConfigureMenu(MenuState). The steps to unit test are pretty
simple.
- Create the screen Presenter class under test
- Set any necessary state on the Presenter
- Call ConfigureMenu(MenuState)
- Lastly, write Assert's that check the expectation of the actual MenuState
On my previous project we created a custom assertion for testing the
MenuState calculation that I thought made the test code fairly descriptive.
ICommand you to...
Now, let's talk about how to tell a MenuItem what to do. Each MenuItem is
going to do very different things and interact with different services and
modules of the application, but we still want to have a consistent mechanism for
attaching actions to MenuItem's. We could use anonymous delegates (and I'm
doing this quite happily in parts of StoryTeller), but that syntax can quickly
lead to ugly code. Instead, let's adopt a Command pattern approach to wrap up
each unique action.
I think one of the fundamental truths of software development is that every
codebase wants, nay, demands, an ICommand interface like this one:
/// <summary>
/// I think I've had some sort of ICommand interface
/// in almost every codebase I've worked on in the last
/// 5 years
/// </summary>
public interface ICommand
{ void Execute();
}
Using a Command pattern comes with several advantages. Foremost in my mind
is the ability to detach the action into a small concrete unit divorced from a
particular screen or UI widget that is easy to test in isolation. Each of our
Command classes should be relatively simple to test. The ICommand classes are
likely manipulating and interacting with various services and other parts of the
application. For easy unit testing, we're probably going to use some sort of Test Double to take the
place of these dependencies. I typically use Constructor Injection to attach
the test doubles.
Here's an example command:
public class SaveCommand : ICommand
{ private readonly IRepository _repository;
private readonly IEventPublisher _publisher;
// SaveCommand needs access to the Singleton instance of both
// IRepository and IEventPublisher. We'll let StructureMap
// deal with wiring up the dependencies
public SaveCommand(IRepository repository, IEventPublisher publisher)
{ _repository = repository;
_publisher = publisher;
}
public void Execute()
{ // Save whatever it is that we're saving
}
}
Inside the unit test harness for SaveCommand I'll simply use RhinoMocks to
create mock objects for IRepository and IEventPublisher:
[TestFixture]
public class SaveCommandTester
{ private MockRepository _mocks;
private IRepository _repository;
private IEventPublisher _publisher;
private SaveCommand _command;
/// <summary>
/// In this method, set up all of the mock objects,
/// and construct an instance of SaveCommand using
/// the two mock objects
/// </summary>
[SetUp]
public void SetUp()
{ _mocks = new MockRepository();
_repository = _mocks.CreateMock<IRepository>();
_publisher = _mocks.CreateMock<IEventPublisher>();
_command = new SaveCommand(_repository, _publisher);
}
}
Assuming that you're comfortable with mock objects, SaveCommand is now
relatively easy to unit test. Of course we're still left with the problem of how
SaveCommand gets the proper instances of IEventPublisher and IRepository in the
real application mode.
Use StructureMap to Wire Up Commands (and other things)
If you've got yourself a reference to an ICommand object you know exactly
what to do to make it work. Without knowing the slightest thing about its
internals, you just call Execute() on the ICommand and get out of the way.
Let's stress part of that again. The MenuItem and its associated controllers
don't need to know anything about the internals of an ICommand object, and they
especially don't need to know how to construct and configure an ICommand
object. Looking again at the configuration code, all we do is "tell" each
MenuItem controller the name of an ICommand to run.
_menuController.MenuItem(saveItem).Executes(CommandNames.Save)
Looking back at our SaveCommand object above, we see that it has a dependency
upon both an IEventPublisher and an IRepository interface, but the code above
doesn't need to specify these two things. To make things a little more
complicated, both of these interfaces are probably a standin for singleton
concrete instances (I use Robert
C. Martin's Just Create One pattern for "Managed
Singleton's" with StructureMap instead of using traditional Singleton's).
Tracking and attaching dependencies doesn't have to be a terrible chore because
we can use tools like StructureMap to help us out.
The first step is to register or configure the proper instances of the
underlying services with StructureMap in one of the normal ways like this code
below:
public class ServiceRegistry : Registry
{ protected override void configure()
{ BuildInstancesOf<IEventPublisher>()
.TheDefaultIsConcreteType<EventPublisher>()
.AsSingletons();
}
}
Now that we've got our services configured we can turn our attention to the
ICommand classes. When we configure the ICommand objects with StructureMap we
also need to associate the ICommand Type's with the correct CommandNames
(CommandNames is a just a strong typed enumeration, the code is at the very
bottom of the post) instance. I use a separate Registry class for the
ICommand's to put the configuration into a common spot and also to create a
custom syntax specific to registering ICommand's.
public class CommandRegistry : Registry
{ protected override void configure()
{ // Wire up the ICommand's
Command(CommandNames.Save).Is<SaveCommand>();
Command(CommandNames.Open).Is<OpenCommand>();
}
}
For the most part, all I need to do is just say that an instance of
CommandNames on the left is the concrete class on the right. It's important to
associate the ICommand classes with an instance of CommandNames because we're
going to retrieve ICommand's in the controller classes with this code:
ICommand command = ObjectFactory.GetNamedInstance<ICommand>(_command.Name);
This Fluent Interface grammar is just a thin veneer over the StructureMap
configuration API. The grammar is implemented in additional members and an
inner class of the CommandRegistry:
private RegisterCommandExpression Command(CommandNames name)
{ return new RegisterCommandExpression(name, this);
}
internal class RegisterCommandExpression
{ private readonly CommandNames _name;
private readonly CommandRegistry _registry;
public RegisterCommandExpression(CommandNames name, CommandRegistry registry)
{ _name = name;
_registry = registry;
}
public void Is<T>()
{ // Register the ICommand type with StructureMap
_registry.AddInstanceOf<ICommand>().UsingConcreteType<T>().WithName(_name.Name);
}
}
Wait, you might say. How does the IEventPublisher and IRepository
dependencies get into SaveCommand? We didn't make any kind of definition or
configuration between SaveCommand and its services. The short answer is that we
don't have to do anything else because StructureMap supports "auto wiring" of
dependencies. StructureMap knows what SaveCommand needs by its constructor
function:
public SaveCommand(IRepository repository, IEventPublisher publisher)
{ _repository = repository;
_publisher = publisher;
}
If you don't explicitly configure an instance of IRepository/IEventPublisher
for SaveCommand StructureMap will happily substitute the default instance of
both types into the constructor function of SaveCommand. While you can always
take full control of the dependency chaining, I find it very convenient just to
let StructureMap deal with it.
* Come on, I'm not the only person that screamed and threw the book across
the room when Roland ends up back at the very beginning. I'm going to swear off
fantasy books permanently if Rand doesn't win a clear cut victory in book 12
whenever that comes out. Almost 20 years worth of waiting better come with a
really solid ending.
MicroController - One MenuItem at a Time
There's a lot of commonality between the menu items. Sure, the individual
actions and rules are different, but there's a finite set of things we need to
do with and to the individual menu items. You could just use the visual
designer to generate one off code for each of the menu items and hard code the
menu on/off rules, but that's going to lead to sheer ugliness. Instead of one
off code, let's create a MicroController class for a single MenuItem.
In a stunning fit of creativity I've named this class MenuItemController. In
this design, MenuItemController has just two responsibilities:
- Setting up the Click event for the MenuItem
- Enabling or disabling the MenuItem
First, let's setup a single MenuItemController and see the code that sets the
Click event.
public class MenuItemController
{ private readonly MenuItem _item;
private readonly CommandNames _command;
private bool _alwaysEnabled = false;
private List<string> _roles = new List<string>();
public MenuItemController(MenuItem item, CommandNames command)
{ _item = item;
_command = command;
_item.Click += new EventHandler(_item_Click);
}
private void _item_Click(object sender, EventArgs e)
{ ICommand command = ObjectFactory.GetNamedInstance<ICommand>(_command.Name);
command.Execute();
}
// Other methods are below...
}
We construct a MenuItemController first with a MenuItem and a CommandNames
key. The constructor simply sets a field for both values then adds an event
handler to the MenuItem's Click event. Inside the _item_Click() event handler
the MenuItemController simply fetches the named ICommand from StructureMap (the
call to ObjectFactory.GetNamedInstance()) and calls Execute() on the ICommand
that comes back. Great, that's the easy part. Great that's the easy part. Now
we can tackle the responsibility for enabling or disabling the MenuItem's.
The MenuItemController class uses three sources of information to make the
enabled determination. The first two sources are optional setters on
MenuItemController.
public bool AlwaysEnabled
{ get { return _alwaysEnabled; } set { _alwaysEnabled = value; } }
public void AddRoles(string[] roles)
{ _roles.AddRange(roles);
}
In some cases you have MenuItem/Command's that should be available in all
states. The "AlwaysEnabled" flag on MenuItemController will short circuit any
other logic and force the MenuItem to be enabled. The second determination is
role-based authorization. Our MenuItemController class keeps a list of the
roles that have access to this action. If there are no roles defined, we'll
assume that the command is accessible to all users.
The third piece of information the MicroController uses to determine menu
state is the screen specific rules that are transmitted in the MenuState object
created by each Presenter. The code to enable or disable the internal MenuItem
inside of a MenuItemController is below. The entry point is Enable(MenuState)
at the top.
public void Enable(MenuState state)
{ _item.Enabled = IsEnabled(state);
}
public bool IsEnabled(MenuState state)
{ if (AlwaysEnabled)
{ return true;
}
if (!state.IsEnabled(_command))
{ return false;
}
return HasRole(state);
}
public bool HasRole(MenuState state)
{ List<string> roles = new List<string>(_roles);
roles.AddRange(state.GetRolesFor(_command));
return hasRole(roles);
}
private static bool hasRole(List<string> roles)
{ if (roles.Count == 0)
{ return true;
}
IPrincipal principal = Thread.CurrentPrincipal;
foreach (string role in roles)
{ if (principal.IsInRole(role))
{ return true;
}
}
return false;
}
Aggregating the MicroController's
By itself, the MicroController classes don't know very much. The
MenuController below aggregates the MenuItemController objects and would
ostensibly give you access to a particular MenuItemController upon demand. In
the SetState(MenuState) method it simply iterates through all of the
MenuItemController objects and calls each individual
MenuItemController.Enable(MenuState) method.
public class MenuController
{ private Dictionary<CommandNames, MenuItemController> _items =
new Dictionary<CommandNames, MenuItemController>();
public void SetState(MenuState state)
{ foreach (KeyValuePair<CommandNames, MenuItemController> item in _items)
{ item.Value.Enable(state);
}
}
// Other methods to support the Fluent Interface configuration
}
Building the Fluent Interface
MenuController above also includes the code for the configuration API. I'm
not sure how every one else is building these things, but I typically use
"Expression" classes that encapsulate the configuration. You can recognize
these things pretty quickly by looking for a lot of "return this;" calls. The
Expression classes are typically pretty dumb. All I do with these classes is
set properties on some sort of inner object that does the actual work. The
MenuItemExpression class below sets properties on a single MenuItemController as
additional methods are called in the configuration. I tend to use inner classes
for the Expression's to get easy access to the private members of the class
being configured. MenuItemExpression is an inner class of MenuController.
public MenuItemExpression MenuItem(MenuItem item)
{ return new MenuItemExpression(this, item);
}
public class MenuItemExpression
{ private readonly MenuController _controller;
private readonly MenuItem _item;
private MenuItemController _itemController;
internal MenuItemExpression(MenuController controller, MenuItem item)
{ _controller = controller;
_item = item;
}
public MenuItemExpression Executes(CommandNames name)
{ _itemController = new MenuItemController(_item, name);
_controller._items.Add(name, _itemController);
return this;
}
public MenuItemExpression IsAlwaysEnabled()
{ _itemController.AlwaysEnabled = true;
return this;
}
public MenuItemExpression IsAvailableToRoles(params string[] roles)
{ _itemController.AddRoles(roles);
return this;
}
}
Wrapping it Up
I think MicroController's and Fluent Interface's go together quite well. The
MicroController's do the work, but the Fluent Interface API can make the code so
much more readable. What I'm finding is that it does take a bit of upfront work
to get a Fluent Interface put together, but once it's set, it's relatively easy
to work with. There's some Architect
Hubris lurking in that statement perhaps. I suppose I might caution you to
utilize a Fluent Interface mostly in situations where you can recoup the upfront
investment with lots of reuse or the API pays off when that code changes
frequently.
I will write at least one more post on this subject just to present the data
binding replacement we're using on my current project (it sounds nuts, but
actually, I'm feeling pretty good about that right now). I wrote about the
larval stages in My
Crackpot WinForms Idea.
I've received a gratifying number of compliments on this series, but I've
consistently heard a common refrain in the negative -- there's no code to
download and it's not clear how all the pieces fit together. To address that
problem I'll change my direction a bit, but it means that "Build your own CAB"
is going on hiatus for at least a month. I'm going to concentrate my "stuck on
the train" time with getting StoryTeller into a usable state. I ended up
scrapping big pieces of the StoryTeller UI and rebuilding with some of the ideas
that I've developed while writing this series. As soon as it's released, I can
use StoryTeller for more complete examples with code that's freely
available.
Appendix: CommandNames
I used CommandNames several times in the course of the post but didn't really
explain it. All I've done is create a Java-style strongly typed enumeration for
the command names. StructureMap only understands strings for instance
identification within a family of like instances, so CommandNames exposes a Name
property. I suppose that you could just use an enumeration and do ToString()
on the keys as appropriate, but I chose this approach for some reason that
escapes my mind at the moment. The code for CommandNames is:
public class CommandNames
{ public static CommandNames Open = new CommandNames("Open"); public static CommandNames Execute = new CommandNames("Execute"); public static CommandNames Export = new CommandNames("Export"); public static CommandNames Save = new CommandNames("Save");
private readonly string _name;
private CommandNames(string name)
{ _name = name;
}
public string Name
{ get { return _name; } }
public override bool Equals(object obj)
{ if (this == obj) return true;
CommandNames commandNames = obj as CommandNames;
if (commandNames == null) return false;
return Equals(_name, commandNames._name);
}
public override int GetHashCode()
{ return _name != null ? _name.GetHashCode() : 0;
}
}
* Come on, I'm not the only person that screamed and threw the book across
the room when Roland ends up back at the very beginning. I'm going to swear off
fantasy books permanently if Rand doesn't win a clear cut victory in book 12
whenever that comes out. Almost 20 years worth of waiting better come with a
really solid ending.