I have the following interface
interface IConsoleHistory
{
void Add(string entry);
HistoryEntry GetNextEntry();
HistoryEntry GetPreviousEntry();
void ResetHistoryMarker();
void Delete(HistoryEntry entry);
void DeleteEntireHistory();
}
public class HistoryEntry
{
public HistoryEntry(string value, int index, bool isCommand)
{
Value = value;
Index = index;
IsCommand = isCommand;
}
public string Value { get; private set; }
public int Index { get; private set; }
public bool IsCommand { get; private set; }
}
Based on that, I implemented an InMemoryHistory:
public class InMemoryHistory : IConsoleHistory
{
protected List<string> History { get; private set; }
private int _currentIndex;
public InMemoryHistory() :this(new List<string>())
{
}
protected InMemoryHistory(List<string> history)
{
History = history;
_currentIndex = -1;
}
public virtual void Add(string entry)
{
History.Insert(0, entry);
}
public HistoryEntry GetNextEntry()
{
if (GetHighestIndex() > _currentIndex)
{
_currentIndex++;
return ReturnAtIndex(_currentIndex);
}
return null;
}
private int GetHighestIndex()
{
return History.Count - 1;
}
private int GetLowestIndex()
{
return History.Count > 0 ? 0 : -1;
}
public HistoryEntry GetPreviousEntry()
{
if (_currentIndex > GetLowestIndex())
{
_currentIndex--;
return ReturnAtIndex(_currentIndex);
}
_currentIndex = -1;
return null;
}
private HistoryEntry ReturnAtIndex(int index)
{
return new HistoryEntry(History[index], index, false);
}
public void ResetHistoryMarker()
{
_currentIndex = -1;
}
public void Delete(HistoryEntry entry)
{
if (History.ElementAtOrDefault(entry.Index) != null)
{
History.RemoveAt(entry.Index);
}
}
public void DeleteEntireHistory()
{
History.Clear();
}
}
Now I wanted to have a file based history. To keep the code DRY I wanted to inherit from the InMemoryHistory and just persist the whole List after every addition.
public class FileBasedHistory : InMemoryHistory
{
private readonly string _fileName;
public FileBasedHistory():this("history.txt")
{
}
public FileBasedHistory(string fileName) :base(GetHistoryFromFile(fileName))
{
_fileName = fileName;
}
public override void Add(string entry)
{
base.Add(entry);
WriteToDisk();
}
private void WriteToDisk()
{
using(var textWriter = new StreamWriter(_fileName, false, Encoding.UTF8))
{
History.ForEach(textWriter.WriteLine);
}
}
private static List<string> GetHistoryFromFile(string fileName)
{
if (!File.Exists(fileName))
return new List<string>();
return File
.ReadAllLines(fileName)
.ToList();
}
}
That works like a charme. What bothers me though is that I need the static GetHistoryFromFile method. It's not really a big deal but I wonder if I'm missing a pattern that would be more appropriate for this situation?
UPDATE
As Keith already suggested. It's also the inheritance approach that kinda bothers me. Inheritance should always be a question of is a.
You can not say: "A FileBasedHistory is a InMemoryHistory"
So I wonder if I should try to use the StrategyPattern for this. Or maybe write an AbstractConsole that implements parts of the logic but leaves room for extensions. Any suggestion on how to refactor it?
I find it odd you are passing in a list as a constructor. You don't have to do it that way at all...
rather than thinking of your GetHistoryFromFile as creating a new list, think of it as a method to load into an existing list ( it becomes more generally useful that way also... as it could load multiple files into a history ).
Also removing and clearing don't work properly for writing to disk...
Also writing a line by line to disk is likely to get quite slow...
Also, your InMemory and File based storage may be suffering from Coincidental coupling. Meaning while they are similarish at the moment, there's a likely chance for them to diverge. eg, if your disk based system used rolling history files and cached history. So don't get too attached to the InMemory and File to remain in a inheritance structure, it may be easier to break them apart
I think you've got it just perfect. GetHistoryFromFile only applies to a FileBasedHistory, so it makes sense that it should be there.
You can use Iterator here. These three methods used only for iterating over your data:
HistoryEntry GetNextEntry();
HistoryEntry GetPreviousEntry();
void ResetHistoryMarker();
And these methods are for managing data:
void Add(string entry);
void Delete(HistoryEntry entry);
void DeleteEntireHistory();
I think this is a different responsibilities, and I'd moved them to different classes.
Related
I am trying to create a custom dictionary with some methods. I created a struct containing the information for lanes in my game. One information tells me if there is an enemy in the lane(Occupied) and the other if we completed that lane so no more enemies will come there(Completed).
I am able to get the initial information out, but cannot update them with my methods. I construct it by adding all 7 lanes, where none of them are either occupied or completed. Then throughout my game, I would like to mark them either as completed or occupied or free, but even after a lot of time searching around, I couldn't figure out the proper way to call for an update of these items inside my laneInfo property.
public struct laneInfo
{
public bool Occupied;
public bool Completed;
}
public class laneInfoClass : Dictionary<int, laneInfo>
{
public laneInfo laneinfo;
public laneInfoClass()
{
for(int i = 0; i <= 6; i++)
{
this.Add(i, false, false);
}
}
public void Add(int key, bool occupied, bool completed)
{
laneinfo.Occupied = occupied;
laneinfo.Completed = completed;
this.Add(key, laneinfo);
}
public void Complete()
{
laneinfo.Completed = true;
}
public void Occupy()
{
laneinfo.Occupied = true;
}
public void Free()
{
laneinfo.Occupied = false;
}
}
Thanks!
Its fairly rare that your class would inherit from a dictionary/list/collection (why?), more often than not what you are actually modelling is a class which has an instance member which is that same dictionary/list/collection.
In addition, you need some way to notify your class which particular lane you're trying to update, you use an integer key so work with that:
public struct LaneInfo
{
public bool Occupied {get;set;}
public bool Completed {get;set;}
}
public class LaneInfoContainer
{
private Dictionary<int, LaneInfo> laneInfoDict = new Dictionary<int, LaneInfo>();
public LaneInfoContainer()
{
for(int i = 0; i <= 6; i++)
{
this.Add(i, false, false);
}
}
public void Add(int key, bool occupied, bool completed)
{
var laneInfo = new LaneInfo();
laneinfo.Occupied = occupied;
laneinfo.Completed = completed;
this.laneInfoDict.Add(key, laneinfo);
}
public void Complete(int key)
{
laneInfoDict[key].Completed = true;
}
public void Occupy(int key)
{
laneInfoDict[key].Occupied = true;
}
public void Free(int key)
{
laneInfoDict[key].Occupied = false;
}
}
I suspect you might also need some way to read the info about your lanes too, add methods such as
public bool IsComplete(int key)
{
return laneInfoDict[key].Complete;
}
The answer to your question is that you should not use a Dictionary. Iterating a List<T>.Contains is faster than Dictionary<TKey, TValye> lookup for 7 items, especially if you are accessing them by index integer 0-6.
Part from that, Dictionary<TKey, TValue> is already generic and so there is no need to inherit from it. Sometimes you would wrap it, for various reasons (one might be locking). But if it is just for a few methods you can simply add extension methods to the Dictionary<int, LaneInfo>.
public static class LaneExtensionMethods
{
public static bool IsComplete(this Dictionary<int, LaneInfo> dictionary, int key)
{
return dictionary[key].Complete;
}
}
// Use
var d = new Dictionary<int, LaneInfo>();
var isComplete = d.IsComplete(1);
I often replace the int with a type to avoid bugs and confusion in code. This would in your case also allow for specialized extension methods. Casting has zero CPU cost (its just cosmetics in code).
public enum LaneId : Int32 { }
public static class LaneExtensionMethods
{
public static bool IsComplete(this Dictionary<LaneId, LaneInfo> dictionary, LaneId key)
{
return dictionary[key].Complete;
}
}
// Use
var d = new Dictionary<LaneId, LaneInfo>();
var laneId = (LaneId)1; // We cast from integer to LaneId, but use LaneId type everywhere in our app
var isComplete = d.IsComplete(laneId);
i am doing an app to manage the creation of role-playing sessions, but i am having problems with the section to do rules summaries so the Master doesnt have to be reading the core book every sec, i have the data structures in this way.
User have a list of campaigns, that campaign have a list of scenaries and that scenaries have a list of adventures.
Users -> Lcampaings -> Lscenaries -> Ladventures
Each campaign, scenary or adventure, have resources which contains the list of documents, images, resources etc, and a hashset of summaries.
Campaign/Scenary/Adventure -> Resources -> Ldocuments/LImages/.../HashSet Summaries
ok, so to modify the summaries i have implemented equality and gethashcode
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using System.Windows;
namespace ElEscribaDelDJ.Classes
{
public class Resumenes: INotifyPropertyChanged, IEqualityComparer<Resumenes>
{
private string _nombre;
public string Nombre
{
get { return _nombre; }
set {
_nombre = value;
OnPropertyChanged("Nombre");
}
}
private string _etiquetas;
public string Etiquetas
{
get { return _etiquetas; }
set {
_etiquetas = value;
OnPropertyChanged("Etiquetas");
}
}
private string _descripcion;
public string Descripcion
{
get { return _descripcion; }
set {
_descripcion = value;
OnPropertyChanged("Descripcion");
}
}
private int _pagina;
public int Pagina
{
get { return _pagina; }
set {
if (value <= 0)
_pagina = 1;
else
_pagina = value;
OnPropertyChanged("Pagina");
}
}
private string _manual;
public string Manual
{
get { return _manual; }
set {
_manual = value;
OnPropertyChanged("Manual");
}
}
private string _manualurl;
public string ManualUrl
{
get { return _manualurl; }
set
{
_manualurl = value;
OnPropertyChanged("ManualUrl");
}
}
private string _tipoaventura;
public string TipoAventura
{
get { return _tipoaventura; }
set {
_tipoaventura = value;
OnPropertyChanged("TipoAventura");
}
}
private string _nombretipoaventura;
public string NombreTipoAventura
{
get { return _nombretipoaventura; }
set {
_nombretipoaventura = value;
OnPropertyChanged("NombreTipoAventura");
}
}
private int _indice;
public int Indice
{
get { return _indice; }
set
{
_indice = value;
OnPropertyChanged("Indice");
}
}
private List<int> _indiceslibres;
public List<int> IndicesLibres
{
get { return _indiceslibres; }
set
{
_indiceslibres = value;
OnPropertyChanged("IndicesLibres");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
public bool Equals(Resumenes x, Resumenes y)
{
if (x.Nombre.Equals(y.Nombre) && x.Descripcion.Equals(y.Descripcion))
return true;
else
return false;
}
public int GetHashCode(Resumenes obj)
{
MessageBox.Show("El Hash code es " + obj.Nombre.GetHashCode());
return obj.Nombre.GetHashCode();
}
public Resumenes CopiarValores ()
{
return (Resumenes)this.MemberwiseClone();
}
}
}
(In gethashcode i have the messagebox just to know if was called ofc i know it shouldnt be there)
I am using the name and description of two objects to know if they are equally or not, and for gethashcode the name.
I have done this after read a lot of questions about how it works hashcode and equallity, hashcodeA == hashcodeB means they could be equally so name looks like perfect for that and thats why in equallity i use also description, because if you have same name and same description its mostly the same summary.
Ok, so i show a list of all summaries, the user select one, click edit, in the windows for add or edit i do a not in deep copy of the objects and after that i call for example campaign edit summary, where i delete the old object and add the new one, because i readed that's the best way if you have modified the fields used to make the hashcode.
public void EditarResumen(Resumenes resumenviejo, Resumenes resumennuevo)
{
DatosAplicacion.CampanaSeleccionada.Recursos.Resumenes.Remove(resumenviejo);
DatosAplicacion.CampanaSeleccionada.Recursos.Resumenes.Add(resumennuevo);
RecursosAplicacion.SesionUsuario.ReemplazarCampana();
}
"Datosaplicacion" is a static class which have the campaign, scenary and aventure that the users chose from all of them
using System;
using System.Collections.Generic;
using System.Text;
namespace ElEscribaDelDJ.Classes.Utilidades.Aplicacion
{
public static class DatosAplicacion
{
private static Campana _campana = new Campana();
public static Campana CampanaSeleccionada
{
get { return _campana; }
set { _campana = value; }
}
public static int IndiceCampana;
private static EscenarioCampana _escenarioseleccionado = new EscenarioCampana();
public static EscenarioCampana EscenarioSeleccionado
{
get { return _escenarioseleccionado; }
set { _escenarioseleccionado = value; }
}
public static int IndiceEscenario;
private static Aventura _aventuraseleccionada;
public static Aventura AventuraSeleccionada
{
get { return _aventuraseleccionada; }
set { _aventuraseleccionada = value; }
}
public static int IndiceAventuraSeleccionada;
}
}
resumenviejo (oldsummary) is made with
public Resumenes CopiarValores ()
{
return (Resumenes)this.MemberwiseClone();
}
this should be fine because i dont have any reference object or similar.
But when i debugg the application the remove operation always throw false, and never calls the equality operation neither the gethashcode.
And i don't know what is happening.
I used this article to do the operations https://dotnetcodr.com/2017/01/12/how-to-check-whether-two-hashsets-are-equal-in-c-net-2/#:~:text=Two%20HashSet%20objects%20in%20C#,their%20order%20in%20the%20collection.
I have the full code uploaded to github https://github.com/davidgmd/Proyecto-de-fin-de-grado
You have two methods GetHashCode and Equals
public bool Equals(Resumenes x, Resumenes y)
public int GetHashCode(Resumenes obj)
But they are not overriding the correct methods from the framework so they won't be called. You have to override the following to methods, so that they will be used by the framework
public override bool Equals(object obj) {
if (!(obj is Resumenes)) return false;
var other = obj as Resumenes;
return this.Nombre.Equals(other.Nombre) && this.Descripcion.Equals(other.Descripcion);
}
public override int GetHashCode() {
return this.Nombre.GetHashCode();
}
Note, that this is not really needed. It's just to clarify that this instance is compared with the other object passed in.
EDIT
You can use your overriding of IEqualityComparer<Resumenes> but then you will have to pass it to the constructor of the hashset. But it's quite uncommon for the data object you put into a HashSet to implement IEqualityComparer. Better your Resumenes should implement the IEquatable<T> interface
public class Resumenes: INotifyPropertyChanged, IEquatable<Resumenes> {
public override bool Equals(object obj) { ... }
public bool Equals(Resumenes other) { ... }
public override int GetHashCode() { ... }
}
There are a few things here:
since Nombre is effectively the hash-key, if it changes while the item is in the hash: all bets are off; a simple way to avoid that is to make it read-only
it is very odd to have a leaf type implement IEqualityComparer<T>; I wonder if this is a large part of the problem - especially if you haven't passed a explicit comparer into the hash-set; however, honestly, it would be simpler and preferable to implement IEquatable<T> here:
public class Resumenes : INotifyPropertyChanged, IEquatable<Resumenes>
{
// ...
public override bool Equals(object obj) => obj is Resumenes other && Equals(other);
public bool Equals(Resumenes other)
=> other is not null && other.Nombre == this.Nombre && other.Descripcion == this.Descripcion;
public override int GetHashCode()
=> Nombre.GetHashCode();
}
You can do this with a custom equality comparer, but you'd need to explicitly pass such a comparer into the new HashSet<Resumenes>(comparer) constructor. I would expect this comparer to be a singleton instance of a different type, for example ResumenesComparer.Instance. Using IEquatable<T> is far more obvious and convenient.
I'm not that new to C# but don't have as much experience as in Java.
As you know, in Java, we can access all the private members from outer classes.
So I tried the same thing in C# because I had some fields and methods needed to be accessed from only inside my plugin library and didn't want it to be shown to users. A simple example can be like this.
public static class StaticClass {
public class InstanceClass {
private int oldValue;
public int Value;
}
public static void Backup(InstanceClass ic) {
ic.oldValue = ic.Value;
}
public static void Restore(InstanceClass ic) {
ic.Value = ic.oldValue;
}
}
If I make the field oldValue public, then it'll be mess and look dirty when end users use the plugin. It doesn't have to be an Inner class or in a some specific form. I just want to know if there is any way to control or access private members of an instance from other static classes in the same assembly only by me.
For allowing access only within assembly use internal modifier.
public class InstanceClass {
internal int oldValue;
public int Value;
}
This is not possible in C#. The container class has no special access over the nested class.
You can access private members of the container from the nested class, but not vice versa. The pattern you're trying to use simply isn't used in C# - it's a violation of member accessibility. There are some hacks to force the Java pattern on C# (using reflection or abusing interfaces), but they are just that - hacks.
The "cleanest" approach might look something like this:
public static class StaticClass
{
private interface IInstanceClassInternal
{
int OldValue { get; set; }
}
public sealed class InstanceClass : IInstanceClassInternal
{
int IInstanceClassInternal.OldValue { get; set; }
public int Value;
}
public static void Backup(InstanceClass ic)
{
((IInstanceClassInternal)ic).OldValue = ic.Value;
}
public static void Restore(InstanceClass ic)
{
ic.Value = ((IInstanceClassInternal)ic).OldValue;
}
}
It's obvious that you're trying to write Java in C# - the patterns, the coding style... That's probably a bad idea. Those static methods should probably be extension methods. The "hidden functionality in an object" doesn't quite sit with C#'s notion of OOP - your parent shouldn't have free access to your guts, it should only really have the same public interface everyone else has. After all, that's the whole point of LSP - such tight coupling is quite tricky for any extensibility. Why separate StaticClass from InstanceClass in the first place, if you want StaticClass to mess with InstanceClasses privates? Just make Backup and Restore public members of InstanceClass - or even a part of an interface (perhaps even through explicit implementation, if you want to "hide" it from users of InstanceClass).
You can use the internal access modifier, see https://msdn.microsoft.com/en-us/library/ms173121.aspx
Internal is only visible from inside the assembly
Example: https://dotnetfiddle.net/FNavfE
Have you tried to make it "internal"? It will be available in same dll but not external dll.
public class InstanceClass {
internal int oldValue;
public int Value;
}
Technically, you can use Reflection (if you insist on private field and a static class methods):
using System.Reflection;
...
public static void Backup(InstanceClass ic) {
if (null == ic)
throw new ArgumentNullException("ic");
ic.GetType()
.GetField("oldValue", BindingFlags.NonPublic | BindingFlags.Instance)
.SetValue(ic, ic.Value);
}
public static void Restore(InstanceClass ic) {
if (null == ic)
throw new ArgumentNullException("ic");
ic.Value = (int) (ic.GetType()
.GetField("oldValue", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(ic));
}
however, a much better approach is to change access modifier from private to internal:
public class InstanceClass {
internal int oldValue;
public int Value;
}
Even better solution is to move both Backup and Restore methods into InstanceClass:
public class InstanceClass {
private int oldValue;
public int Value;
public void Backup() {
oldValue = Value;
}
public void Restore() {
Value = oldValue;
}
}
This field oldValue is an implementation detail of both StaticClass and InstanceClass. Lets make InstanceClass an implementation detail of StaticClass and export an interface StaticClass.IInstance to external clients:
public static class StaticClass {
public interface IInstance {
int Value { get; set; }
}
private class InstanceClass: IInstance {
public int oldValue;
public Value { get; set; }
}
// Static class becomes responsible for handing out `IInstance` objects
public static IInstance GetInstance() {
return new InstanceClass();
}
public static void Backup(IInstance i) {
if (i is InstanceClass ic) {
ic.oldValue = ic.Value;
}
else {
throw new InvallidOperationException("Can only Backup IInstance objects that were created by GetInstance");
}
}
public static void Restore(IInstance i) {
if (I is InstanceClass ic)
{
ic.Value = ic.oldValue;
}
else {
throw new InvallidOperationException("Can only Restore IInstance objects that were created by GetInstance");
}
}
This solution is similar to the one Luaan proposes. But instead of using an interface to export private data, it uses an interface to limit the publicly available data; to my opinion this is a cleaner design with less surprises.
It does change Value from a field to a property; so when you really need a field, this pattern does not work.
The static class in the example of OP makes it a bit awkward and having better solutions, but imagine this in a regular class, perhaps in a repository. Working on a repository, where observers should be notified when properties of items in the repository are set and not wanting the items to contain a reference to the repository or to the repositories observers, led me to searching for "method only accessible to container class?" which led me to this question.
I intend to solve it as follows:
public class Repo
{
public interface IItem
{
int Id { get; }
string MyProperty { get; }
}
private class Item
{
public int Id { get; }
public string MyProperty { get; private set; }
public bool TrySetMyProperty(string newValue)
{
if (!Equals(MyProperty, newValue) &&
IsPreconditionValid())
{
MyProperty = newValue;
return true;
}
else
{
return false;
}
IsPreconditionValid() => true;
}
}
public event EventHandler<EventArgs> OnChanged;
private readonly ConcurrentDictionary<int, Item> items = new ConcurrentDictionary<int, Item>();
public IItem GetOrCreateItemById(int id)
{
bool changed = false;
IItem result = items.GetOrAdd(int, CreateItem);
if (changed)
{
OnChanged?.Invoke(this, EventArgs.Empty);
}
return result;
IItem CreateItem(int key)
{
changed = true;
return new Item() { Id = key };
}
}
public bool TrySetItemMyProperty(int id, string newValue)
{
if (items.TryGet(id, out Item i))
{
if (i.TrySetMyProperty(newValue))
{
OnChanged?.Invoke(this, EventArgs.Empty);
return true;
}
}
return false;
}
}
I have class with two properties which are Lists, one of it contents int - that's IDs of objects from second List. I override setters and getters to save them agreeable with each other. But when I add some this to list they are not synchronized. How to make them synchronized?
Here is code
public class Item
{
private List<Operation> _operations = new List<Operation>();
private List<int> _operationsID = new List<int>();
public List<Operation> operations
{
get { return this._operations; }
set
{
this._operations = value;
if (value != null)
{
foreach (Operation oper in value)
{
this._operationsID.Add(oper.ID);
}
}
}
}
public List<int> operationsID
{
get { return this._operationsID; }
set
{
this._operationsID = value;
if (value != null)
{
foreach (int operID in value)
{
this._operations.Add(new Operation(operID));
}
}
}
}
}
Should I override List.Add if so, how it can me made?
It is a bit unclear what it is you are trying to do, but basically it seems like you need to encapsulate those lists so the user can't work on them directly (and get them out of sync). You do this by not exposing the lists to the user. Basically you are trying to keep the items contained to the user so whenever they work on your set of items, they would be forced to go through this class and the functions that class exposes. Your only issue then is to find out what to expose to the user and in what manner.
public class Item {
private List<Operation> _operations = new List<Operation>();
private List<int> _operationsID = new List<int>();
public void addOperation(Operation o) {
_operations.Add(o);
_operationsID.Add(getIdentifier(o));
}
public void removeOperation(Operation o) {
_operations.Remove(o);
_operationsID.Remove(getIdentifier(o));
}
public void clear() {
_operations.clear();
_operationsID.clear();
}
public void findOperationMatching(Foobar foo) {
//
}
private int getIdentifier(Operation id) {
//
}
}
You have to clear previous list content before calling Add method:
public List<Operation> operations
{
get { return this._operations; }
set
{
this._operations = value;
if (value != null)
{
this._operationsID.Clear();
foreach (Operation oper in value)
{
this._operationsID.Add(oper.ID);
}
}
else
{
this._operationsID = null;
}
}
}
But to be honest, I don't think it's a good idea to keep these things in two different lists. Why don't you use Dictionary<int, Operation>?
It's a bad idea to try to manage two versions of the truth. If it were me, I'd expose one List<Operation> that callers can Add/Remove, and a second IEnumerable<int> which simply exposes the ID's of the operations:
public List<Operation> Operations { get; set; }
public IEnumerable<int> OperationIDs
{
get
{
return Operations.Select(op => op.OperationID);
}
}
This way, callers can use the Operations list to do whatever they need to do (Add, Remove, Count, etc). The OperationIDs is now not a second property that people can work with; instead it only reflects information that is in the Operations property.
Consider the following control (snipped for brevity):
public partial class ConfigurationManagerControl : UserControl
{
public Func<string, bool> CanEdit { get; set;}
public Func<string, bool> CanDelete { get; set; }
public Dictionary<string, string> Settings
{
get { return InnerSettings; }
set
{
InnerSettings = value;
BindData();
}
}
private Dictionary<string, string> InnerSettings;
private void OnListIndexChanged(object sender, EventArgs e)
{
this.EditButton.Enabled = false;
this.DeleteButton.Enabled = false;
var indices = this.List.SelectedIndices;
if (indices.Count != 1)
{
return;
}
var index = indices[0];
var item = this.List.Items[index];
if (this.CanEdit != null)
{
this.EditButton.Enabled = this.CanEdit(item.Text);
}
if (this.CanDelete != null)
{
this.DeleteButton.Enabled = this.CanDelete(item.Text);
}
}
}
There's more to this control, but suffice it to say that it allows a user to add, edit, and delete the entries in a Dictionary<string, string>. In order to determine whether or not it should allow the user to edit or delete the entries, it uses the delegate method properties, CanDelete and CanEdit, which are provided by the form or control that hosts it:
public class SetupWizard : Form
{
public SetupWizard()
{
InitializeComponent();
this.SettingManager.CanEdit = CanEditSetting;
this.SettingManager.CanDelete = CanDeleteSetting;
}
private static bool CanEditSetting(string item)
{
var lockedSettings = new[] { "LicenseHash", "ProductHash" };
return !lockedSettings.Contains(item.ToLower());
}
private static bool CanDeleteSetting(string item)
{
var lockedSettings = new[] {
"LicenseHash",
"ProductHash",
"UserName",
"CompanyName"
};
return !lockedSettings.Contains(item.ToLower());
}
}
I find that this design is both satisfactory and worrisome at the same time. On the one hand, it seems to solve the problem using the simplest solution that works (it certainly separates the concerns nicely). On the other hand, I have this nagging concern that I am using delegates improperly and should be using an event, instead (even though I do not need multiple listeners, and only need the caller to tell me if the item is editable).
And then, on the other other hand, there's the chance that there's a completely different design that I haven't even considered that might solve the problem in a vastly superior way.
So. Is this design technically correct, maintainable, and flexible? Or should I be doing something better?
I suggest the use of an interface with these two methods. That's a lot cleaner:
interface ICantThinkOfAGoodName
{
bool CanEdit(string item);
bool CanDelete(string item);
}
You could create something similar to the RelayCommand used in many MVVM frameworks:
public class RelayObject : ICantThinkOfAGoodName
{
public RelayObject() : this(null, null) {}
public RelayObject(Func<string, bool> canEdit, Func<string, bool> canDelete)
{
if(canEdit == null) canEdit = s => true;
if(canDelete == null) canDelete = s => true;
_canEdit = canEdit;
_canDelete = canDelete;
}
public bool CanEdit(string item)
{
return _canEdit(item);
}
public bool CanDelete(string item)
{
return _canDelete(item);
}
}
Use it like this:
public SetupWizard()
{
InitializeComponent();
this.SettingManager.PropertyName = new RelayObject(CanEditSetting,
CanDeleteSetting);
// or (all can be deleted)
this.SettingManager.PropertyName = new RelayObject(CanEditSetting, null);
// or (all can be edited)
this.SettingManager.PropertyName = new RelayObject(null, CanDeleteSetting);
// or (all can be edited and deleted)
this.SettingManager.PropertyName = new RelayObject();
}
BTW: I am using Property injection here, because it is a control. Normally, I would pass the ICantThinkOfAGoodName dependency in the constructor of the ConfigurationManagerControl.
It may be this is what #Daniel Hilgarth is suggesting when he says "use an interface" (n.b. - his answer now reflects a more general/flexible approach to implementing the interface). Instead of assigning delegates to your method directly, why not give the control a property, such as DataState or whatever you want to call it, using an interface that encapsulates the information you need, and leave it up to the owner to decide how to implement that.
interface IDataState
{
bool CanEdit(string item);
bool CanDelete(string item);
}
public partial class ConfigurationManagerControl : UserControl
{
public IDataState DataState {get;set;}
// your code checks DataState.CanEdit & DataState.CanDelete
}
public class SetupWizard : Form, IDataState
{
public SetupWizard()
{
InitializeComponent();
SettingManager.DataState =this;
}
public bool CanEdit(string item)
{
... implement directly or return from your private function
}
public bool CanDelete(string item)
{
}
}
But this gives you the flexibility to implement that interface any way you choose, with another object, etc. and it makes it easy to also just pass the owner itself (implementing the interface).