解决这个问题的类似线程可能就在SO上,但我不理解它们:(
我有这样的场景:
internal static void AddToListBox(ListBox lb, User usr)
{
if (CanAddUser(lb, usr))
lb.Items.Add(usr);
}
static bool CanAddUser(ListBox lb, User usr)
{
foreach (User u in lb.Items)
{
if (u.Id == usr.Id)
return false;
}
return true;
}
现在我有另一个完全类似的情况:
internal static void AddToList(List<User> lstUser, User usr)
{
if (CanAddUser(lstUser, usr))
lstUser.Add(usr);
}
static bool CanAddUser(List<User> lstUser, User usr)
{
foreach (User u in lstUser)
{
if (u.Id == usr.Id)
return false;
}
return true;
}
基本上两组都做同样的事情。我很好奇,如果我可以使它成为一个函数,但这只是它的工作原理,这使代码更加冗长:
internal static void AddToList(IEnumerable enumerable, User usr)
{
if (enumerable is ListBox.ObjectCollection)
{
if (CanAddUser(enumerable, usr))
((ListBox.ObjectCollection)enumerable).Add(usr);
}
else if (enumerable is List<User>)
{
if (CanAddUser(enumerable, usr))
((List<User>)enumerable).Add(usr);
}
}
internal static bool CanAddUser(IEnumerable enumerable, User usr)
{
foreach (User u in enumerable)
{
if (u.Id == usr.Id)
return false;
}
return true;
}
正如我所说的那样有效,但if else块使它看起来很丑陋并且杀死了缩短代码的想法。我可以做这样的事情:
internal static void AddToList(IEnumerable enumerable, User usr)
{
if (CanAddUser(enumerable, usr))
((whatever the type is passed, just find it yourself)enumerable).Add(usr);
}
...
我试过
internal static void AddToList(IEnumerable enumerable, User usr)
{
if (CanAddUser(enumerable, usr))
((enumerable.GetType()) / (typeof(enumerable))enumerable).Add(usr);
//both which wont compile.
}
那怎么办呢?是否无法将IEnumerable
转换为未知类型?或者解决方案是否太乱了我应该放弃计划?如果我知道传递的IEnumerable
只有ListBox.ObjectCollection
和List<User>
而不是if else逻辑,那么可以做任何事情吗?很抱歉这篇冗长的文章只是需要解释我的要求..
UPDATE: StriplingWarrior的答案必然会解决我的问题,但我想知道如果我要实现最后一个函数,对象是如何转换为其原始类型的。可能通过反思或其他东西。我会将解决方案标记为答案。再次感谢@StriplingWarrior。
更新2:
为了更清楚,忘记上面发布的所有内容。假设我有类似下面的功能。 Admonish()
是我在List<Naughty>
和List<Obedient>
类型的对象上分别定义的扩展方法。我如何才能只编写一个Sanitize
函数并将List<Naughty
或List<Obedient
传递给它?像这样:
internal static void Sanitize(IEnumerable enumerable)
{
enumerable.Admonish();
}
...
我知道我可以这样做:
internal static void Sanitize(IEnumerable enumerable)
{
if (enumarable is List<Naughty>)
((List<Naughty>)enumerable).Admonish();
if (enumarable is List<Obedient>)
((List<Obedient>)enumerable).Admonish();
}
我将如何一次性完成这项工作:
internal static void Sanitize(IEnumerable enumerable)
{
((Determine type here, but how??)enumerable).Admonish();
}
希望问题很清楚。我不是在寻找一个特殊的案例解决方案,而是在运行时获取类型的一般解决方案,以便可以将对象转换为其类型 !!
答案 0 :(得分:3)
由于ListBox.ObjectCollection
实现了IList
接口,因此您应该能够在两种情况下都使用该接口。
internal static void AddToList(IList list, User usr)
{
if (!list.Cast<User>().Any(u => u.Id == user.Id))
list.Add(usr);
}
这对IEnumerable
不起作用的唯一原因是因为该界面没有Add
方法。
这个问题比初看起来要复杂得多,答案取决于你的Admonish方法的作用。它只是迭代列表并在列表的每个成员上执行特定的操作吗?
public static void Admonish(this IEnumerable<Naughty> followers)
{
foreach(var follower in followers) { follower.Preach(); }
}
public static void Admonish(this IEnumerable<Obedient> followers)
{
foreach(var follower in followers) { follower.Preach(); }
}
如果是这种情况,你可以让Naughty和Obedient实现一个特定的接口:
public interface IFollower
{
void Preach();
}
public class Naughty : IFollower {...}
public class Obedient : IFollower {...}
public static void Admonish(this IEnumerable<IFollower> followers)
{
foreach(var follower in followers) { follower.Preach(); }
}
internal static void Sanitize(IEnumerable enumerable)
{
enumerable.Cast<IFollower>().Admonish();
}
另一方面,如果Admonish根据你正在看的类型做了不同的事情,那不是那么简单:
public static void Admonish(this IEnumerable<Naughty> followers)
{
foreach(var follower in followers) { follower.Chastise(); }
}
public static void Admonish(this IEnumerable<Obedient> followers)
{
foreach(var follower in followers) { follower.Encourage(); }
}
在这种情况下,自动调用正确的Admonish方法的唯一方法是使用反射来查找和调用它。就.NET框架而言,没有理由相信这两个Admonish
方法有任何关联,所以你不能只假装它们是相同的方法。
如果您愿意使用非静态方法制作Admonish,则可以使用dynamic
关键字来完成或多或少的尝试,但它不适用于扩展方法:< / p>
public class Admonisher
{
public void Admonish(IEnumerable<Naughty> followers)
{
foreach(var follower in followers) { follower.Chastise(); }
}
public void Admonish(IEnumerable<Obedient> followers)
{
foreach(var follower in followers) { follower.Encourage(); }
}
}
internal static void Sanitize(dynamic enumerable)
{
dynamic admonisher = new Admonisher();
admonisher.Admonish(enumerable);
}
但即使这种方法只有在您传入的可枚举由通用IEnumerable<Naughty>
或IEnumerable<Obedient>
支持的情况下才有效。如果你传递ListBox.ObjectCollection
,即使动态运行时框架也无法确定要调用的Admonish方法。
首先让我说反射通常不是这类问题的最佳解决方案。反思很慢,它否定了编译编程语言的大部分优点。但是如果你认为这是要走的路,那么你就是这样做的。假设您的Admonish
方法签名如下所示:
public static void Admonish(this IEnumerable<Naughty> followers)
...并假设您有一个实际实现类型实现IEnumerable<Naughty>
的列表:
IList list = new List<Naughty>(); // List<Naughty> implements IEnumerable<Naughty>
...你可以找到并调用这样的方法:
var utilClass = typeof(Utility); // the class with your admonish methods
var admonishMethod =
(from m in utilClass.GetMethods()
where m.IsStatic && m.Name == "Admonish"
let parameter = m.GetParameters()[0]
where parameter.ParameterType.IsAssignableFrom(list.GetType())
select m).Single();
admonishMethod.Invoke(null, new[]{list});