I am trying attempting to refactor the following code:
public static void SaveSplitbar(RadSplitBar splitBar)
{
Logger.InfoFormat("Saving splitBar {0}", splitBar.ID);
RadSplitBarSetting splitbarSettings = splitBar.GetSettings();
SerializableDictionary<string, RadSplitBarSetting> splitBarStates = GetStates<SerializableDictionary<string, RadSplitBarSetting>>();
bool splitbarIsKnown = splitBarStates.ContainsKey(splitbarSettings.ID);
if (splitbarIsKnown)
{
Logger.Debug("SplitBar is known. Overwriting data.");
splitBarStates[splitbarSettings.ID] = splitbarSettings;
}
else
{
Logger.Debug("SplitBar is unknown. Saving data.");
splitBarStates.Add(splitbarSettings.ID, splitbarSettings);
}
RadControlManager.SetStates<SerializableDictionary<string, RadSplitBarSetting>>(splitBarStates);
}
The logic of the code is reused in saving various other objects. I am trying to make this code more generic so that I will be able to have one Save() method rather than SaveX(), SaveY(), and SaveZ() all performing the same work on different objects.
I have a bit of a snag, however. While all of the objects being passed into Save have the “GetSettings()” method, many of them have this method defined in an extension method:
//RadControlExtensions.cs Extension Methods
public static RadSplitBarSetting GetSettings(this RadSplitBar splitbar)
{
RadSplitBarSetting radSplitbarSetting = new RadSplitBarSetting(splitbar.ID, splitbar.Parent.ID, splitbar.CollapseMode, splitbar.AdjacentPanesNames);
return radSplitbarSetting;
}
As such, when I tried to refactor SaveSplitbar, I realize that I would have to be switching on the type of my parameter. When I started going down this route I thought, “Well, this is stupid, if I have to determine the type of the object I should just write Save multiple times as extension methods for each object.”
While this is an “OK” solution, I am unhappy with it. The logic behind saving each type of control is identical and I would prefer not having copy/pasted logic in my extension class.
My next thought was, “Well, if any parameter passed into Save is guaranteed to have a GetSettings method — maybe there is a way to have an IRadControlExtensions interface which exposes this method. I was unable to determine any such way as each GetSettings method has a different signature – does not seem possible to announce it just once in the interface.
So that’s where I am at. How should I be approaching this problem?
EDIT: Another Save method
public static void SavePane(CormantRadPane pane)
{
Logger.InfoFormat("Saving pane {0}", pane.ID);
RadPaneSetting paneSettings = pane.GetSettings();
SerializableDictionary<string, RadPaneSetting> paneStates = GetStates<SerializableDictionary<string, RadPaneSetting>>();
bool paneIsKnown = paneStates.ContainsKey(paneSettings.ID);
if (paneIsKnown)
{
Logger.Debug("Pane is known. Overwriting data.");
paneStates[paneSettings.ID] = paneSettings;
}
else
{
Logger.Debug("Pane is unknown. Saving data.");
paneStates.Add(paneSettings.ID, paneSettings);
}
RadControlManager.SetStates<SerializableDictionary<string, RadPaneSetting>>(paneStates);
}
EDIT2: Perhaps a better question is, “When is it time to stop extending a class and time to start creating a new class which inherits from the class currently being extended?”
Firstly i would have each type implement a marker interface with the required properties and methods for this operation, something like
where IWidget looking something like
and each Settings class with something like ISetting
But then your problem comes in with the extention methods, which one should be resolved for the correct concreate IWidget. To get that type you could simply use.
Remember extention methods is actually compiler trickery. The compiler automatically emits ExtensionAttribute on extension methods when using the this keyword.
Taken from some funky code from Jon Skeet here you could scan the assembly for the correct extention method, and invoke it.
Jon said..
Hope this helps 🙂