概括需要处理不同数据成员的算法

Generalize an algorithm that needs to work on different data members

提问人:sbi 提问时间:10/7/2010 最后编辑:sbi 更新时间:10/8/2010 访问量:271

问:

我有一段代码是这样的:

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

(我需要稍微调整一下才能发布,所以如果我搞砸了,请耐心等待。

现在,我需要为另一个成员(具有相关但不同的类型)和另一个创建函数提供这段确切的代码。我知道我可以获取此代码,复制它,替换一些标识符,然后完成。但我觉得这样做很脏。我觉得应该有一种方法来推广这个小算法。someVar

我知道我总是可以将创建函数作为委托传递,但我不知道如何概括成员访问权限,然后是这些成员(和创建函数)具有不同类型的问题。

在 C++ 中,我会使用成员指针和模板来执行此操作。成员指针并不是小菜一碟,但是一旦我查找了它们奇怪的语法,我就会在几分钟内完成。如何在C#中做到这一点?

编辑:由于这似乎还不够清楚,因此下面是同一算法的其他实例的样子:

void someOtherAlgorithm(SomeOtherType someVar) // 1 change here
{
  someVar.someOtherMember = createSomeOtherValue(); // 2 changes here
  lock(something)
  {
    SomeOtherType someOtherVar = something.Find(someVar.unrelatedMember);
    if(someOtherVar != null)
      someOtherVar.someOtherMember = someVar.someOtherMember; // 2 changes here
  }
}

我希望这能澄清这一点。

c#

评论

0赞 NullUserException 10/8/2010
和方法体一模一样?
0赞 sbi 10/8/2010
@NullUserException:是的,除了所有用法都用 和 代替。someMembersomeOtherMembercreateSomeValue()createSomeOtherValue()
0赞 SLaks 10/8/2010
与 ?someOtherVarsomeVar
0赞 sbi 10/8/2010
@SLaks:“......它有一个相关但不同的类型......”
0赞 Brian 10/8/2010
您可能会发现 stackoverflow.com/questions/2980463/ 中的代码与您的关注点相关。

答:

1赞 SLaks 10/8/2010 #1

您可以传递用于创建和设置值的 lambda 表达式。

例如:

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赞 sbi 10/8/2010
请再读一遍我的问题。我添加了该算法的第二个实例。我还需要更改“传递 lambdas”行,以使其几乎不可读。此外,这是否解释了不同类型的不同数据成员?someOtherVar.someMember = someVar.someMember;
0赞 SLaks 10/8/2010
您可以传递另一个 lambda(或重用第一个 lambda 是类型和属性相同)。泛型允许使用不同的类型。someOtherVar
0赞 Dan Tao 10/8/2010
在我看来,当可以是任意的时,该部分将无法真正起作用(除非您添加强制从某个基类派生的通用约束)。someVar.SomeOtherMembersomeVarTObjectTObject
0赞 SLaks 10/8/2010
@Dan:正确。将需要约束或额外的 lamdba。
3赞 Tomas Petricek 10/8/2010 #2

我认为最好的选择是使用委托并将一个简单的选择器函数作为参数传递给算法。可以使该方法泛型,以便选择器可以返回任何类型的成员:Func

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

然后你可以写这样的东西:

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

如果没有关于实际代码的更多细节,就很难精确地写出答案(例如,您可能需要对泛型类型参数进行一些约束 - 例如 如果要比较这些值),但这通常是解决问题的最佳方法。IComparable

如果您需要在设置/获取值的代码中进行另一个参数化,那么您只需添加其他函数即可。例如(设置值)和另一个成员的“选择器”函数。在调用中,这将是 .如果具有不同的类型,则可能需要再添加一个类型参数。someMemberAction<SomeType, T>(s, val) => s.SomeMember = valsomeMember

评论

1赞 Doc Brown 10/8/2010
我建议您添加另一个示例,说明如何以通用方式向someMember进行分配。
0赞 sbi 10/8/2010
请再读一遍我的问题:而不是我需要.你的对此无济于事。该算法几乎与我的问题中所示完全相同。someMembersomeOtherMember
3赞 Dan Tao 10/8/2010 #3

你让我有点困惑,那里。你接受一个调用类型的参数,并声明一个同名的局部变量(所以它不能按原样编译)。从你的两个定义来看,和似乎是相同的类型(),但你的局部变量只用 声明,所以不完全清楚它们是否是。someAlgorithmSomeTypesomeOtherVarsomeVarsomeOtherVarSomeTypevar

在你对 SLaks 的评论中,你暗示了 和 是不同的类型(即使在你引用的问题部分,你说的是不同的成员,而 SLaks 问的是你的两个变量和)。因此,我将假设它们是不同的类型,这只是一个局部变量,而不是参数。someVarsomeOtherVarsomeVarsomeVarsomeOtherVarsomeOtherVar

基于这些假设:

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

对于您的第一个算法,这将称为:

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

对于您的第二个:

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

评论

0赞 sbi 10/8/2010
很抱歉造成混乱。当我试图提取该算法时,我怀疑我搞砸了,但是,当然,我没有看到它......无论如何,感谢您(正确地)推断我需要什么的努力。看来你已经找到了做我想做的事的方法(从我这里),尽管正如我所担心的那样:维护复制的代码几乎比这更容易。 好吧,到目前为止,该算法已经有了相当大的发展,并且还有两个具有完全相同的问题,因此无论如何,这似乎是值得的。+1:(
0赞 Dan Tao 10/8/2010
@sbi:关于这段代码的可维护性:我同意,所有这些委托很快就会变得非常麻烦。出于这个原因,我建议可能定义一些接口,这些接口可以按照您想要的方式处理此算法中的不同对象。这样,您的算法可以接受提供 4 个类似方法的接口的 a 和实例,而不是接受 a 和 4 (!) 委托。SomeTypeSomeType