1

基本クラスは、キーと値MasterClassを含む Dictionary を保持します。ここで、(派生クラスから) 変数 Type を保持し、その get/set メソッドへの参照を保持します。stringHookObjHookObj

基本クラス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;
    }
}

ゲッター メソッドの使用について説明する必要はありません。それでは、これがエンドユーザーにとってどれほど厄介になるかを見てみましょう。

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
}

しかし、後で使用するために参照を保存することはできないようです..これをよりユーザーフレンドリーにする方法はありますか? 変数ごとに 2 つの関数を作成すると、非常に面倒になると思います。

4

3 に答える 3

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;
  }

}
于 2012-10-02T22:23:45.407 に答える
1

@Guffaの回答に似たようなことをしましたが、プロパティの「フック」ラッパーを保持するオブジェクトの代わりに、ラムダを使用して、元の値型を使用してプロパティを取得/設定しました。

マスタークラス:

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ため、すべての解析ロジックを割り当てプロセスと混同することはありません。

編集:カスタムパーサーを使用して実装をまとめ、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);
    }
}

カスタムフックパーサーハンドラー

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は少しの作業を使用できます。具体的には、 ( newHookAndAddを置き換えたRegisterProperty)メソッドは作業を複製しており、サポートする「標準」フックタイプごとにエントリを追加する必要があると思います。より良い方法があると確信していますが、今のところ、どのフックが使用されているかを気にせずに「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));
    }
}

およびプロパティはネイティブstringintサポートされています。RegisterProperty彼らは彼らのタイプに結びついた過負荷にぶつかった。ただし、boolタイプはネイティブでサポートされていないため、独自の解析ロジックを提供します。「yesno」は、文字列が「yes」と等しいことを確認するだけです。「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すべてのオーバーロードがコードの重複が少なく、より単純なものに折りたたまれます(ただし、まだ完全には正しく感じられません)。

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."
于 2012-10-02T22:38:05.153 に答える
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";
    }
}
于 2012-10-02T22:13:15.503 に答える