问题描述
|
我正在尝试重构以下代码:
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);
}
代码的逻辑可在保存各种其他对象时重用。我试图使此代码更通用,以便能够使用一个Save()方法,而不是SaveX(),SaveY()和SaveZ()在不同对象上执行相同工作。
但是,我有一点障碍。尽管所有传递给Save的对象都具有\“ GetSettings()\”方法,但其中许多对象在扩展方法中定义了以下方法:
//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;
}
这样,当我尝试重构SaveSplitbar时,我意识到我将不得不打开参数的类型。当我开始沿着这条路线走的时候,我想:“好吧,这很愚蠢,如果我必须确定对象的类型,我应该为每个对象多次写保存作为扩展方法。”
虽然这是一个“确定”的解决方案,但我对此并不满意。保存每种控件的背后逻辑是相同的,我希望在扩展类中不要复制/粘贴逻辑。
我的下一个想法是,\“好吧,如果确保传递给Save的任何参数都保证具有GetSettings方法,也许有一种方法可以使IRadControlExtensions接口公开此方法。我无法确定每种GetSettings之类的方法方法具有不同的签名-似乎不可能在界面中仅宣布一次。
这就是我的位置。我应该如何解决这个问题?
编辑:另一种保存方法
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:也许更好的问题是,“何时停止扩展类,何时开始创建从当前正在扩展的类继承的新类?”
解决方法
首先,我将让每种类型都实现一个带有此操作所需的属性和方法的标记接口,例如
public class CormantRadPane : IWidget
{
...
}
public class RadSplitBar : IWidget
{
...
}
IWidget看起来像
public interface IWidget
{
int ID;
}
和每个设置类,例如ISetting
但是,随之而来的问题是扩展方法,对于正确的concreate IWidget应该解决该方法。要获得该类型,您可以简单地使用。
Type contreteWidgetType = widget.GetType();
记住扩展方法实际上是编译器的骗术。使用this关键字时,编译器会自动在扩展方法上发出ExtensionAttribute。
从Jon Skeet的一些时髦代码中获得的代码,您可以在汇编中扫描正确的扩展方法,然后调用它。
琼恩说..
查找用ExtensionAttribute装饰的类,然后查找该类中也用ExtensionAttribute装饰的方法。然后检查第一个参数的类型,以查看它是否与您感兴趣的类型匹配。
static IEnumerable<MethodInfo> GetExtensionMethods(Assembly assembly,Type extendedType)
{
var query = from type in assembly.GetTypes()
where type.IsSealed && !type.IsGenericType && !type.IsNested
from method in type.GetMethods(BindingFlags.Static
| BindingFlags.Public | BindingFlags.NonPublic)
where method.IsDefined(typeof(ExtensionAttribute),false)
where method.GetParameters()[0].ParameterType == extendedType
select method;
return query;
}
希望这可以帮助 :)
, 关键是不要对要保存的内容通用……您实际上并不在乎其类型是什么。您只需要该实例即可为您提供某种设置对象(您确实在乎设置的类型!)
public static void Save<TSetting>(IGetSetting<TSetting> saveMe)
where TSetting : IHasID
{
TSetting setting = saveMe.GetSetting();
SerializableDictionary<string,TSetting> states =
GetStates<SerializableDictionary<string,TSetting>>();
bool isKnown = states.ContainsKey(setting.ID);
if (isKnown)
{
states[setting.ID] = setting;
}
else
{
states.Add(setting.ID,setting);
}
RadControlManager.SetStates<SerializableDictionary<string,TSetting>>(states);
}
interface IGetSetting<TSetting>
{
TSetting GetSetting();
}
interface IHasID
{
string ID {get;}
}
class RadSplitBar : IGetSetting<RadSplitBarSetting>
class RadSplitBarSetting : IHasID
如果您不拥有RadSplitBar来应用IGetSetting接口,请考虑采用这种方法来使调用方说出所需的内容:
public static void Save<TSetting>(Func<TSetting> howToGetSetting)
{
TSetting setting = howToGetSetting();
//... rest as above.
}
致电者
SaveExtensions.Save( radSplitBar.GetSettings );