0

我的情况是这样的。我需要在多种不同类型的对象上运行一些验证和按摩类型代码,但是为了清洁(和代码重用),我想让这个验证的所有调用看起来基本相同,无论对象如何。我试图通过重载来解决这个问题,在我得到 Generic Collection 对象之前它工作得很好。

下面的例子应该澄清我在这里谈论的内容:

private string DoStuff(string tmp) { ... }

private ObjectA DoStuff(ObjectA tmp) { ... }

private ObjectB DoStuff(ObjectB tmp) { ... }

...

private Collection<ObjectA> DoStuff(Collection<ObjectA> tmp) {
    foreach (ObjectA obj in tmp) if (DoStuff(obj) == null) tmp.Remove(obj);
    if (tmp.Count == 0) return null;
    return tmp;
}

private Collection<Object> DoStuff(Collection<ObjectB> tmp) {
    foreach (ObjectB obj in tmp) if (DoStuff(obj) == null) tmp.Remove(obj);
    if (tmp.Count == 0) return null;
    return tmp;
}

...

Collection<T>这似乎是一种真正的浪费,因为我必须为每种不同的类型复制完全相同的代码。我想制作一个DoStuff处理 any的单个实例Collection<T>,而不是为每个实例制作一个单独的实例。

我试过使用ICollection,但这有两个问题:第一,ICollection不暴露.Remove方法,我不能写foreach循环,因为我不知道列表中对象的类型。使用更通用的东西,比如object,不起作用,因为我没有DoStuff接受的方法object- 我需要它为实际对象调用适当的方法。编写一个DoStuff方法,它需要一个object并执行某种巨大的 if 语句列表来选择正确的方法并适当地进行转换,这违背了摆脱冗余代码的整个想法——我还不如复制和粘贴所有这些Collection<T>方法。

我尝试过使用通用DoStuff<T>方法,但这在foreach循环中有同样的问题。因为我在设计时不知道对象类型,所以编译器不会让我调用DoStuff(obj).

从技术上讲,编译器应该能够在编译时判断需要进行哪个调用,因为这些都是私有方法,并且在调用中传递的对象的特定类型在方法被调用时都是已知的。该知识似乎并没有出现在此方法调用的后续方法中。

我真的不想在这里使用反射,因为这会使代码比复制和粘贴所有Collection<T>方法更加复杂,并且会降低性能。有任何想法吗?

--- 编辑 1--- 我意识到我的通用方法引用没有正确显示,因为我没有使用尖括号的 html 代码。现在应该解决这个问题。

--- 编辑 2 --- 根据下面的回复,我将Collection<T>方法更改为如下所示:

private Collection<T> DoStuff<T>(Collection<T> tmp) {
    for (int i = tmp.Count - 1; i >= 0; i--) if (DoStuff(tmp[i]) == null) tmp.RemoveAt(i);
    if (tmp.Count == 0) return null;
    return tmp;
}

但是,这仍然不起作用,因为当我调用DoStuff(tmp[i]).

4

2 回答 2

0

您需要将要调用的方法作为参数传递给泛型方法。这样,重载决议发生在编译器知道期望什么类型的地方。

或者,您需要使每个项目的 DoStuff 方法通用(或对象)以支持集合中任何可能的项目。

(我还将 RemoveItem 调用与第一个循环分开,这样它就不会试图从被迭代的同一个列表中删除一个项目。)

private Collection<T> DoStuff<T>(Collection<T> tmp, Func<T, T> stuffDoer)
{
    var removeList = tmp
        .Select(v => stuffDoer(v))
        .Where(v => v == null)
        .ToList();

    foreach (var removeItem in removeList) tmp.Remove(removeItem);

    if (tmp.Count == 0) return null;
    return tmp;
}

private class ObjectA { }
private class ObjectB { }

private string DoStuff(string tmp) { return tmp; }

private ObjectA DoStuff(ObjectA tmp) { return tmp; }

private ObjectB DoStuff(ObjectB tmp) { return tmp; }

使用此代码调用:

var x = new Collection<ObjectA>
{
    new ObjectA(),
    new ObjectA(),
    null
};

var result = DoStuff(x, DoStuff);
于 2018-08-27T18:10:48.527 回答
-1

像这样的东西?:

private Collection DoStuff<T>(Collection tmp) 
{
    // This will probably assert as you are modifying a collection while looping in it.
    foreach (T obj in tmp) if (DoStuff(obj) == null) tmp.Remove(obj); 
    if (tmp.Count == 0) return null;
    return tmp;
}

其中 T 是集合中对象的类型。

请注意,您有一条很可能会断言的行。所以:

private Collection DoStuff<T>(Collection tmp) 
{
    // foreach doesn't work if you are modifying the collection.
    // Looping backward with an index, so we never encounter an invalid index.
    for (int i = tmp.Count - 1; i >= 0; i--) if (DoStuff(tmp[i]) == null) tmp.Remove(tmp[i]);
    if (tmp.Count == 0) return null;
    return tmp;
}

但是在这一点上......为什么让它通用,因为你不再使用 T 了?

private Collection DoStuff(Collection tmp) 
{
    // DoStuff can be generic, but you shouldn't need to explicitly pass it a type...
    for (int i = tmp.Count - 1; i >= 0; i--) if (DoStuff(tmp[i]) == null) tmp.Remove(tmp[i]);
    if (tmp.Count == 0) return null;
    return tmp;
}
于 2012-10-25T21:30:29.497 回答