C# 不使用getter和setter方法为基类访问子类变量
基类C# 不使用getter和setter方法为基类访问子类变量,c#,inheritance,C#,Inheritance,基类MasterClass保存一个包含string键和HookObj值的字典,其中HookObj保存(来自派生类)变量类型及其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, s
MasterClass
保存一个包含string
键和HookObj
值的字典,其中HookObj
保存(来自派生类)变量类型及其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;
}
}
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;
}
}
我希望它更像这样:
protected override void hookMethod()
{
newHookAndAdd(ref userID, "userID");
//etc
}
但似乎不可能存储引用供以后使用。。有没有办法让它更方便用户?我猜,为每个变量创建两个函数会变得非常混乱。为什么会有烟雾和镜子?像这样直截了当有什么不对 建议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";
}
}
公共抽象类基类
{
受保护的只读字典值=新建字典();
}
公共类子类:基类
{
公共儿童班()
{
值[“UserID”]=123;
值[“Foo”]=“Bar”;
}
}
您可以创建一个泛型钩子
类来保存值,以及一个抽象的钩子
基类来保存类型,以便主类
可以在不知道钩子的实际类型的情况下获取类型:
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;
}
}
公共类主类{
私人词典;
公共钩子AddHook(字符串名称,T值){
吊钩=新吊钩(值);
_添加(名称、钩);
回程钩;
}
public void receiveData(字符串键、字符串值){
吊钩;
如果(_dict.TryGetValue(键,钩外)){
if(hook.\u type==typeof(string)){
(钩子作为钩子)。值=值;
}else if(hook.\u type==typeof(int)){
(hook作为hook.Value=Int32.Parse(Value);
}否则{
抛出新的NotImplementedException();//找不到类型
}
}否则{
抛出新的NotImplementedException();//未找到名称
}
}
}
公共抽象类钩子{
内型(u型),;
内钩(类型){
_类型=类型;
}
}
公共类钩子:钩子{
公共T值{get;set;}
公共挂钩(T值):基础(类型(T)){
价值=价值;
}
}
现在,用户可以创建Hook
对象来保存值:
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;
}
}
class附件变量:MasterClass{
私有钩子用户ID;
私用钩位;
私有Attachvariable():base(){
userId=AddHook(“userId”,0);
position=AddHook(“position”,String.Empty);
}
public Attachvariable(int-id,string-pos):this(){
userId.Value=id;
位置值=位置;
}
}
我做了一些类似于@Guffa的回答的事情,但我没有使用对象为属性保存“钩子”包装,而是使用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);
}
}
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);
}
}
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));
}
}
您甚至可以设置钩子注册者以提供解析器:
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
实现,这样您就不会将所有解析逻辑与赋值过程混为一谈
编辑:我最终使用自定义解析器创建了一个实现,并放弃了if/elseif/elseif类型检查:
钩子基类
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);
}
}
本机支持字符串
和int
属性;他们点击与他们的类型相关的注册表属性
重载。但是,bool
类型本机不受支持,因此它们提供自己的解析逻辑。“yesno”只是检查字符串是否等于“yes”。“ProperBoolean”执行标准的布尔解析。用法如下所示:
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,只需向下传递解析方法,但通过这种方式,您可以创建新的标准钩子,这些钩子可能具有更复杂的解析逻辑,因此应该更易于阅读/实现。不管怎么说,我很快就解决了这个问题,如果我在生产中使用它,我会花更多的时间来清理/改进/测试
BIIIIG的一个优点是,这使得对单个钩子类型实现进行单元测试非常容易。可能会重构主类
,以使单元测试更容易(尽管现在非常简单),可能会将钩子创建者移动到单独的工厂/构建器中
编辑:见鬼,现在只需抛出HookObj
基上的type
字段,并将其替换为一个接口:
public interface IHookObj
{
void SetValue(string value);
object GetValue();
}
您可以在整个过程中传播更改(HookObj
不再调用传入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
是:
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;
}
}
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;
}
}
结果可能如下所示:
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."
newHookAndAdd方法在哪里
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;
}
}
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."