基类MasterClass
包含一个包含string
键和HookObj
值的Dictionary,其中HookObj
保存(来自派生类)变量Type和引用它是get / set方法。
现在,当基类MasterClass
从某个源接收数据时,它会像这样转换/分配它:
//Data came in from an external source, see if we know what variable to assign the value to
public void receiveData(string key, string value)
{
if (dict.ContainsKey(key))
assignVal(dict[key], value);
else
throw new NotImplementedException(); //use NotImplementedException as placeholder until we make proper exception
}
//Cast the value-string to the proper type and assign it
private void assignVal(HookObj hookobj, string value)
{
try
{
if (hookobj.theType == typeof(string))
hookobj.setMethod(value);
else if (hookobj.theType == typeof(int))
hookobj.setMethod(Int32.Parse(value));
else if (hookobj.theType == typeof(float))
hookobj.setMethod(float.Parse(value));
else
throw new NotImplementedException();
}
catch (RuntimeBinderException ex) { throw new NotImplementedException("", ex); }
catch (System.FormatException ex) { throw new NotImplementedException("", ex); }
}
HookObj
为:
internal class HookObj
{
public Type theType { get; private set; }
public Action<dynamic> setMethod { get; private set; }
public Func<dynamic> getMethod { get; private set; }
public HookObj(Type theType, Action<dynamic> setMethod, Func<dynamic> getMethod)
{
this.theType = theType;
this.setMethod = setMethod;
this.getMethod = getMethod;
}
}
使用getter方法不需要explenation。现在让我们看看这对最终用户来说有多么混乱:
class Attachvariable : MasterClass
{
public Attachvariable(int id) : base(id) { }
public Attachvariable(int userID, string position)
: base()
{
this.userID = userID;
this.position = position;
}
int userID;
string position;
protected override void hookMethod()
{
newHookAndAdd(typeof(int), set_userID, get_userID, "userID").
newHookAndAdd(typeof(string), set_position, get_position, "position");
}
public void set_userID(dynamic theVal)
{
userID = theVal;
}
public void set_position(dynamic theVal)
{
position = theVal;
}
public dynamic get_userID()
{
return userID;
}
public dynamic get_position()
{
return position;
}
}
我希望它更像是这样:
protected override void hookMethod()
{
newHookAndAdd(ref userID, "userID");
//etc
}
但是它不可能存储引用供以后使用它似乎..有没有办法让这个用户更友好?每个变量创建两个函数会让我觉得非常混乱。
答案 0 :(得分:1)
您可以创建一个通用的Hook<T>
类来保存该值,并为它保留一个类型的抽象Hook
基类,以便MasterClass
可以在不知道的情况下获取该类型实际的钩子类型:
public class MasterClass {
private Dictionary<string, Hook> _dict;
public Hook<T> AddHook<T>(string name, T value){
Hook<T> hook = new Hook<T>(value);
_dict.Add(name, hook);
return hook;
}
public void receiveData(string key, string value) {
Hook hook;
if (_dict.TryGetValue(key, out hook)) {
if (hook._type == typeof(string)) {
(hook as Hook<string>).Value = value;
} else if (hook._type == typeof(int)) {
(hook as Hook<int>).Value = Int32.Parse(value);
} else {
throw new NotImplementedException(); // type not found
}
} else {
throw new NotImplementedException(); // name not found
}
}
}
public abstract class Hook {
internal Type _type;
internal Hook(Type type) {
_type = type;
}
}
public class Hook<T> : Hook {
public T Value { get; set; }
public Hook(T value) : base(typeof(T)){
Value = value;
}
}
现在,用户可以创建Hook<T>
个对象来保存值:
class Attachvariable : MasterClass {
private Hook<int> userId;
private Hook<string> position;
private Attachvariable() : base() {
userId = AddHook("userID", 0);
position = AddHook("position", String.Empty);
}
public Attachvariable(int id, string pos) : this() {
userId.Value = id;
position.Value = pos;
}
}
答案 1 :(得分:1)
我做了类似于@Guffa的回答,但是我没有使用包含属性的“Hook”包装器的对象,而是使用lambdas来使用它们的原始值类型来获取/设置属性:
<强>大师班:强>
class MasterClass
{
Dictionary<string, HookObj> dict = new Dictionary<string, HookObj>();
//Data came in from an external source, see if we know what variable to assign the value to
public void receiveData(string key, string value)
{
if (dict.ContainsKey(key))
assignVal(dict[key], value);
else
throw new NotImplementedException(); //use NotImplementedException as placeholder until we make proper exception
}
//Cast the value-string to the proper type and assign it
private void assignVal(HookObj hookobj, string value)
{
try
{
if (hookobj.theType == typeof(string))
hookobj.SetValue(value);
else if (hookobj.theType == typeof(int))
hookobj.SetValue(Int32.Parse(value));
else if (hookobj.theType == typeof(float))
hookobj.SetValue(float.Parse(value));
else
throw new NotImplementedException();
}
catch (RuntimeBinderException ex) { throw new NotImplementedException("", ex); }
catch (System.FormatException ex) { throw new NotImplementedException("", ex); }
}
protected void newHookAndAdd<T>(Action<T> setter, Func<T> getter, string name)
{
HookObj hook = new HookObj<T>(setter, getter);
dict.Add(name, hook);
}
}
<强> HookObj 强>
public class HookObj<T> : HookObj
{
public Action<T> setMethod { get; private set; }
public Func<T> getMethod { get; private set; }
public HookObj(Action<T> setMethod, Func<T> getMethod)
: base(typeof(T))
{
this.setMethod = setMethod;
this.getMethod = getMethod;
}
public override void SetValue(object value)
{
setMethod((T)value);
}
public override object GetValue()
{
return getMethod();
}
}
public abstract class HookObj
{
public Type theType { get; private set; }
public HookObj(Type theType)
{
this.theType = theType;
}
public abstract void SetValue(object value);
public abstract object GetValue();
}
然后你的MasterClass的子类:
class Attachvariable : MasterClass
{
public int UserID { get; set; }
public string Position { get; set; }
public Attachvariable()
{
hookMethod();
}
protected void hookMethod()
{
newHookAndAdd(value => UserID = value, () => UserID, "userID");
newHookAndAdd(value => Position = value, () => Position, "position");
}
}
您甚至可以设置您的钩子注册人以提供解析器:
class YesNoVariable : MasterClass
{
public bool YesNo { get; set; }
public YesNoVariable()
{
hookMethod();
}
protected void hookMethod()
{
newHookAndAdd(value => YesNo = value, () => YesNo, "yesno", (input) => input == "yes");
}
}
我没有通过将解析器作为可选参数添加到基本处理程序的动作,我会留给你,因为它在这个阶段是微不足道的。基本上,您的assignValue
会检查HookObj
是否分配了解析器,如果已分配,则使用它。否则它会经历你已经拥有的相同动作。
事实上,我希望所有的钩子都使用解析器并具有特定的IntHookObj
,FloatHookObj
等等。 newHookAndAdd
方法基本上是一个工厂。如果用户提供自己的解析器,它将使用该自定义解析器创建HookObj
。如果T
是int / float / string,它将实例化一个已知的HookObj
实现,因此您不要将所有解析逻辑与赋值过程混合。
挂钩基类
public abstract class HookObj<T> : HookObj
{
public Action<T> setMethod { get; private set; }
public Func<T> getMethod { get; private set; }
protected HookObj()
: base(typeof(T))
{
}
public void SetSetter(Action<T> setMethod)
{
this.setMethod = setMethod;
}
public void SetGetter(Func<T> getMethod)
{
this.getMethod = getMethod;
}
protected abstract T Parse(string value);
public override void SetValue(string value)
{
T parsedValue = Parse(value);
setMethod(parsedValue);
}
public override object GetValue()
{
return getMethod();
}
}
public abstract class HookObj
{
public Type theType { get; private set; }
public HookObj(Type theType)
{
this.theType = theType;
}
public abstract void SetValue(string value);
public abstract object GetValue();
}
标准挂钩实施
public class StringHook : HookObj<string>
{
protected override string Parse(string value)
{
return value;
}
}
public class IntHook : HookObj<int>
{
protected override int Parse(string value)
{
return Int32.Parse(value);
}
}
public class FloatHook : HookObj<float>
{
protected override float Parse(string value)
{
return float.Parse(value);
}
}
自定义挂钩解析器处理程序
public class CustomHook<T> : HookObj<T>
{
public Func<string, T> inputParser { get; private set; }
public CustomHook(Func<string, T> parser)
{
if (parser == null)
throw new ArgumentNullException("parser");
this.inputParser = parser;
}
protected override T Parse(string value)
{
return inputParser(value);
}
}
<强>大师班:强>
class MasterClass
{
Dictionary<string, HookObj> dict = new Dictionary<string, HookObj>();
//Data came in from an external source, see if we know what variable to assign the value to
public void receiveData(string key, string value)
{
if (dict.ContainsKey(key))
assignVal(dict[key], value);
else
throw new NotImplementedException(); //use NotImplementedException as placeholder until we make proper exception
}
//Cast the value-string to the proper type and assign it
private void assignVal(HookObj hookobj, string value)
{
hookobj.SetValue(value);
}
protected void RegisterProperty<T>(Action<T> setter, Func<T> getter, string name, Func<string, T> inputParser)
{
var hook = new CustomHook<T>(inputParser);
hook.SetSetter(setter);
hook.SetGetter(getter);
dict.Add(name, hook);
}
protected void RegisterProperty(Action<string> setter, Func<string> getter, string name)
{
var hook = new StringHook();
hook.SetSetter(setter);
hook.SetGetter(getter);
dict.Add(name, hook);
}
protected void RegisterProperty(Action<int> setter, Func<int> getter, string name)
{
var hook = new IntHook();
hook.SetSetter(setter);
hook.SetGetter(getter);
dict.Add(name, hook);
}
protected void RegisterProperty(Action<float> setter, Func<float> getter, string name)
{
var hook = new FloatHook();
hook.SetSetter(setter);
hook.SetGetter(getter);
dict.Add(name, hook);
}
}
现在,MasterClass可以使用一些工作。具体来说,我感觉 RegisterProperty
方法(替换newHookAndAdd)是重复工作,你必须为你支持的每个“标准”钩子类型添加条目。我确信有一个更好的方法,但是现在它提供了你的“Attachvariable”子类,不关心使用什么钩子,他们知道本地支持哪些类型,以及它的类型安全。本地不支持的任何类型,必须提供类型安全的解析器。
附加变量样本
class Attachvariable : MasterClass
{
public int UserID { get; set; }
public string Position { get; set; }
public bool YesNo { get; set; }
public bool ProperBoolean { get; set; }
public Attachvariable()
{
RegisterProperties();
}
protected void RegisterProperties()
{
RegisterProperty(value => UserID = value, () => UserID, "userID");
RegisterProperty(value => Position = value, () => Position, "position");
RegisterProperty(value => YesNo = value, () => YesNo, "yesno", (input) => input == "yes");
RegisterProperty(value => ProperBoolean = value, () => ProperBoolean, "ProperBoolean", (input) => Boolean.Parse(input));
}
}
原生支持string
和int
属性;他们遇到与他们的类型相关的RegisterProperty
重载。但是,本地不支持bool
类型,因此它们提供了自己的解析逻辑。 “yesno”只是检查字符串是否等于“是”。 “ProperBoolean”执行标准Boolean.Parse
。用法如下:
Attachvariable obj = new Attachvariable();
obj.receiveData("userID", "9001");
obj.receiveData("position", "Hello World!");
obj.receiveData("yesno", "yes");
obj.receiveData("ProperBoolean", "True");
Console.WriteLine(obj.UserID); //9001
Console.WriteLine(obj.Position); //"Hello World!"
Console.WriteLine(obj.YesNo); //True
Console.WriteLine(obj.ProperBoolean); //True
obj.receiveData("yesno", "something else!");
Console.WriteLine(obj.YesNo); //False
obj.receiveData("ProperBoolean", "Invalid Boolean!"); //throws exception on `Boolean.Parse` step as intended
我想过让“标准”钩子从CustomHook继承而只是向下传递解析方法,但是这样你就可以创建新的标准钩子,这可能有更复杂的解析逻辑,所以这应该让它更容易阅读/实行。无论如何,我鞭打了这个,如果我在生产中使用它,我会花更多的时间来清理/改进/测试。
biiiiiiiig plus这使得非常容易对您的各个钩子类型实现进行单元测试。可能可能重构MasterClass
以便更容易进行单元测试(尽管现在非常简单),也许可以将钩子创建者移动到一个单独的工厂/构建器中。
编辑:哎呀,现在只是扔掉theType
基地上的HookObj
字段并用接口替换它:
public interface IHookObj
{
void SetValue(string value);
object GetValue();
}
您可以在整个过程中传播更改(HookObj<T>
不再调用传入typeof(T)
的基础构造函数,MasterClass
现在绑定到IHookObj
接口。这意味着您甚至可以更轻松地定义使用他们想要的任何逻辑,解析或其他方式的钩子,并且应该更容易测试。
编辑:是的,这是一个示例实现,其中API的第三方使用者可以提供他们自己的钩子,可以在他们的应用程序中重复使用。如果他们在任何地方都使用Person
个对象,他们只需定义一个钩子就可以重用:
class SomeCustomUsage : MasterClass
{
public Person SomeoneUnimportant { get; set; }
public SomeCustomUsage()
{
RegisterProperty(value => SomeoneUnimportant = value, () => SomeoneUnimportant, "SomeoneUnimportant", new PersonHook());
}
}
他们的PersonHook
是:
public class PersonHook : HookObj<Person>
{
protected override Person Parse(string value)
{
string[] parts = value.Split(',');
var person = new Person(parts[0], parts[1]);
if (person.FirstName == "Bob")
throw new Exception("You have a silly name and I don't like you.");
if (String.IsNullOrWhiteSpace(person.FirstName))
throw new Exception("No first name provided.");
if (String.IsNullOrWhiteSpace(person.LastName))
throw new Exception("No last name provided.");
return person;
}
}
向HookObj<T>
提供RegisterProperty
重载也将所有重载都折叠为更简单的代码重复(但仍然没有感觉正确):< / p>
protected void RegisterProperty<T>(Action<T> setter, Func<T> getter, string name, HookObj<T> hook)
{
hook.SetSetter(setter);
hook.SetGetter(getter);
dict.Add(name, hook);
}
protected void RegisterProperty<T>(Action<T> setter, Func<T> getter, string name, Func<string, T> inputParser)
{
var hook = new CustomHook<T>(inputParser);
RegisterProperty(setter, getter, name, hook);
}
protected void RegisterProperty(Action<string> setter, Func<string> getter, string name)
{
var hook = new StringHook();
RegisterProperty(setter, getter, name, hook);
}
protected void RegisterProperty(Action<int> setter, Func<int> getter, string name)
{
var hook = new IntHook();
RegisterProperty(setter, getter, name, hook);
}
protected void RegisterProperty(Action<float> setter, Func<float> getter, string name)
{
var hook = new FloatHook();
RegisterProperty(setter, getter, name, hook);
}
结果可能如下:
SomeCustomUsage customObj = new SomeCustomUsage();
customObj.receiveData("SomeoneUnimportant", "John,Doe");
Console.WriteLine(customObj.SomeoneUnimportant.LastName + ", " + customObj.SomeoneUnimportant.FirstName); //Doe, John
customObj.receiveData("SomeoneUnimportant", "Bob,Marley"); //exception: "You have a silly name and I don't like you."
customObj.receiveData("SomeoneUnimportant", ",Doe"); //exception: "No first name provided."
customObj.receiveData("SomeoneUnimportant", "John,"); //exception: "No last name provided."
答案 2 :(得分:0)
为什么所有的烟雾和镜子?像这样更直接的事情出了什么问题?
建议1
public abstract class BaseClass
{
protected virtual int UserId { get; set; }
}
public class ChildClass : BaseClass
{
private int _userId;
protected override int UserId
{
get { return _userId; }
set { _userId = value; }
}
}
建议2
public abstract class BaseClass
{
protected readonly Dictionary<string, object> Values = new Dictionary<string, object>();
}
public class ChildClass : BaseClass
{
public ChildClass()
{
Values["UserID"] = 123;
Values["Foo"] = "Bar";
}
}