开发者

Generalize an algorithm that needs to work on different data members

I have a piece of code that is like this:

void someAlgorithm(SomeType someVar)
{
  someVar.someMember = createSomeValue();
  lock(something)
  {
    SomeType someOtherVar = something.Find(someVar.unrelatedMember);
    if(someOtherVar != null)
      someOtherVar.someMember = someVar.someMember;
  }
}

(I needed to adapt it a bit for posting, so please bear with me if I messed it up in doing so.)

Now I need this exact piece of code for another member of someVar (which has a related, but different type) and another creation function. I know I can just take this code, copy it, replace a few identifiers, and be done. But I feel dirty doing so. I feel there should be a way to generalize this little algorithm.

I know I can always pass the creation function as a delegate, but I don't know how to generalize the member access and then there's the issue of those members (and creation functions) having different types.

In C++, I would use member pointers combined with templates for doing this. Member pointers aren't exactly a piece of cake to use, but once I had looked up their weird syntax, I'd be done within a few minutes. How to do that in C#?

Edit: Since this doesn't seem clear enough, here's what that other instance of the same algorithm looks like:

void someOtherAlgorithm(SomeOtherType someVar) // 1 change here
{
  someVar.someOtherMe开发者_如何学编程mber = createSomeOtherValue(); // 2 changes here
  lock(something)
  {
    SomeOtherType someOtherVar = something.Find(someVar.unrelatedMember);
    if(someOtherVar != null)
      someOtherVar.someOtherMember = someVar.someOtherMember; // 2 changes here
  }
}

I hope this clarifies this.


You've confused me a bit, there. Your someAlgorithm takes a parameter of type SomeType called someOtherVar and declares a local variable with the same name (so it can't compile as is). From your two definitions it appears that someVar and someOtherVar are the same type (SomeType), but your local variable is only declared with var, so it isn't entirely clear whether they are or not.

In your comment to SLaks you imply that someVar and someOtherVar are of different types (even though in the part of your question that you quoted you were talking about different members of someVar while SLaks was asking about your two variables someVar and someOtherVar). So I'm going to assume they're different types and that someOtherVar is only a local variable, not a parameter.

Based on these assumptions:

void someAlgorithm<TMember>(
    SomeType someVar,
    Func<TMember> create,                   // replaces "createSomeValue"
    Func<SomeType, TMember> getter,         // replaces get for "someMember"
    Action<SomeType, TMember> setter,       // replaces set for "someMember"
    Action<SomeOtherType, TMember> setter2) // replaces set for "someMember"
                                            // on "someOtherVar" (not necessary
                                            // if "someOtherVar" is actually
                                            // the same type as "someVar")
{
  setter(somevar, create());

  lock(something)
  {
    SomeOtherType someOtherVar = something.Find(someVar.unrelatedMember);

    if(someOtherVar != null)
      setter2(someOtherVar, getter(someVar));
  }
}

For your first algorithm, this would be called like:

someAlgorithm(
    someVar,
    createSomeValue,
    x => x.someMember,
    (x, y) => { x.someMember = y; },
    (x, y) => { x.someMemberOfOtherType = y; }
);

For your second:

someAlgorithm(
    someVar,
    createSomeOtherValue,
    x => x.someOtherMember,
    (x, y) => { x.someOtherMember = y; },
    (x, y) => { x.someOtherMemberOfOtherType = y; }
);


I think the best option is to use a Func delegate and pass a simple selector function as an argument to the algorithm. You can make the method generic, so that the selector can return members of any type:

void someAlgorithm<T>(SomeType someVar, SomeType someOtherVar, 
                      Func<SomeType, T> selector) { 
  someVar.someMember = createSomeValue(); 
  lock(something) { 
    var someOtherVar = something.Find(selector(someVar));  // Use 'selector'
    if(someOtherVar != null) 
      someOtherVar.someMember = someVar.someMember; 
  } 
} 

Then you can write something like:

someAlgorithm(some1, some2, a => a.SomeOtherMember);

Without more details about your actual code, it is a bit difficult to write the answer precisely (e.g. you may need some constraints for the generic type parameter - e.g. IComparable if you're comparing the values), but this is generally the best approach to solving the problem.

If you need another parameterization in the code that sets/gets values of someMember then you would just add other functions. For example Action<SomeType, T> (which sets value) and one more "selector" function for the other member. In the call, this would be (s, val) => s.SomeMember = val. If someMember has a different type, then you'd probably need to add one more type parameter.


You can pass lambda expressions that create and set the values.

For example:

void someAlgorithm<TObject, TProperty>(TObject someVar, Func<TProperty> creator, Action<TObject, TProperty> setter, Action<SomeType, TProperty> relatedSetter)
{
  var value = creator();
  setter(someVar, value);
  lock(something)
  {
    var someOtherVar = something.Find(someVar.SomeOtherMember);
    if(someOtherVar != null)
      relatedSetter(someOtherVar, value);
  }
}


someAlgotihm(something, createSomeValue, (x, v) => x.someProperty = v, (x, v) => x.someProperty = v);
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜