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."