简体   繁体   English

如何在运行时将类型未知的对象强制转换为类型?

[英]How to cast an object of unknown type to its type in run time?

Similar threads that addresses this are possibly there on SO, but I do not understand them :( 解决这个问题的类似线程可能在SO上,但我不理解它们:(

I have a scenario like this: 我有这样的场景:

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

Now I have another exactly similar scenario like this: 现在,我有另一个完全类似的场景,例如:

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

Basically both set does the same thing. 基本上两个集合都做相同的事情。 I was curious if I could make it one function, but this is only how it worked, which makes the code more verbose: 我很好奇我是否可以使它成为一个函数,但这只是它的工作方式,这使代码更加冗长:

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

As I said it works, but the if else block makes it look ugly and kills the idea of shortening the code. 就像我说的那样,它可以工作,但是if else块使它看起来很难看,并且扼杀了缩短代码的想法。 Could I be doing something like this: 我可以做这样的事情:

internal static void AddToList(IEnumerable enumerable, User usr)
{
    if (CanAddUser(enumerable, usr))
        ((whatever the type is passed, just find it yourself)enumerable).Add(usr);
}

?? ??

I tried 我试过了

internal static void AddToList(IEnumerable enumerable, User usr)
{
    if (CanAddUser(enumerable, usr))
        ((enumerable.GetType()) / (typeof(enumerable))enumerable).Add(usr);
    //both which wont compile.
}

So how to go about this? 那怎么去呢? Is it impossible to cast IEnumerable to an unknown type? IEnumerable转换为未知类型是不可能的? Or would the solution be just too messy that I should drop the plan? 还是解决方案太混乱,以至于我不打算放弃该计划? Anything can be done if I know the passed IEnumerable will be only of type ListBox.ObjectCollection and List<User> other than the if else logic? 如果我知道传递的IEnumerable只有ListBox.ObjectCollection类型和List<User>而不是if else逻辑,那么可以做任何事情吗? Sorry for the lengthy piece, just needed to explain my requirement.. 很抱歉这篇冗长的文章,只需要解释我的要求..

UPDATE: StriplingWarrior's answer necessarily solves my problem, but I wonder how would be the object cast to its original type, if I'm to implement the last function. 更新: StriplingWarrior的答案必然解决我的问题,但我想知道如果我要实现最后一个函数,对象是如何转换为其原始类型的。 Possibly via Reflection or something. 可能通过反思或其他东西。 I will mark the solution to this as answer. 我将解决方案标记为答案。 Thanks @StriplingWarrior once again. 再次感谢@StriplingWarrior。

UPDATE 2: To make it more clear, forget all what has been posted above. 更新2:更清楚地说,忘记上面已发布的所有内容。 Suppose I have a function like below. 假设我有如下功能。 Admonish() is an extension method I have defined on objects of type List<Naughty> and List<Obedient> separately. Admonish()是我分别在类型List<Naughty>List<Obedient>对象上定义的扩展方法。 How will I be able to write just one Sanitize function and pass either List<Naughty or List<Obedient to it? 我怎样才能只编写一个Sanitize函数并将List<NaughtyList<Obedient传递给它? Like this: 像这样:

internal static void Sanitize(IEnumerable enumerable)
{
    enumerable.Admonish();
}

?? ??

I know I can do this: 我知道我可以这样做:

internal static void Sanitize(IEnumerable enumerable)
{
    if (enumarable is List<Naughty>)
        ((List<Naughty>)enumerable).Admonish();
    if (enumarable is List<Obedient>)
        ((List<Obedient>)enumerable).Admonish();
}

How will I be able to do it in one go, like this: 我将如何一口气做到这一点,就像这样:

internal static void Sanitize(IEnumerable enumerable)
{
    ((Determine type here, but how??)enumerable).Admonish();
}

Hope question is clear. 希望问题很清楚。 I'm not looking for a special case solution but a general solution to get the type at run time so that an object can be cast to its type !! 我不是在寻找特殊情况的解决方案,而是在运行时获取类型的通用解决方案, 以便可以将对象转换为它的类型

Since ListBox.ObjectCollection implements the IList interface, you should be able to use that interface for both cases. 由于ListBox.ObjectCollection实现了IList接口,因此您应该在两种情况下都可以使用该接口。

internal static void AddToList(IList list, User usr)
{
    if (!list.Cast<User>().Any(u => u.Id == user.Id))
        list.Add(usr);
}

The only reason this didn't work for IEnumerable is because that interface doesn't have an Add method. 不适用于IEnumerable的唯一原因是因为该接口没有Add方法。

Response to Update 2 对Update 2的回应

This question is a lot more complicated than it appears at first glance, and the answer would depend on what your Admonish method does. 这个问题比乍看起来要复杂得多,答案取决于您的告诫方法。 Does it just iterate the list and perform something specific on each member of the list? 它只是迭代列表并在列表的每个成员上执行特定的操作吗?

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

If this is the case, you can just make both Naughty and Obedient implement a specific interface: 在这种情况下,您可以使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();
}

On the other hand, if Admonish does something different depending on what type you're looking at, that's not so simple: 另一方面,如果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(); }
}

In this case, the only way to automatically call the correct Admonish method is to use reflection to find and call it. 在这种情况下,自动调用正确的Admonish方法的唯一方法是使用反射来查找和调用它。 As far as the .NET framework is concerned, there is no reason to believe that the two Admonish methods are in any way related, so you can't just pretend that they're the same method. 就.NET框架而言,没有理由相信这两个Admonish方法有任何关联,所以你不能只假装它们是相同的方法。

If you're willing make Admonish a non-static method, you might use the dynamic keyword to accomplish more or less what you're trying to do, but it won't work on extension methods: 如果您愿意使用非静态方法制作Admonish,则可以使用dynamic关键字来完成或多或少的尝试,但它不适用于扩展方法:

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

But even this approach only works if the enumerable you pass in is backed by a generic IEnumerable<Naughty> or IEnumerable<Obedient> . 但即使这种方法只有在传入的可枚举由通用IEnumerable<Naughty>IEnumerable<Obedient>支持时才有效。 If you pass it a ListBox.ObjectCollection , even the dynamic runtime framework won't be able to figure out which Admonish method to call. 如果你传递一个ListBox.ObjectCollection ,即使动态运行时框架也无法确定要调用哪个Admonish方法。

Finding and calling the correct method via reflection 通过反射找到并调用正确的方法

Let me begin by saying reflection is usually not the best solution for problems like this. 首先,让我说反思通常不是解决此类问题的最佳解决方案。 Reflection is slow, and it negates most of the advantages of compiled programming languages. 反射很慢,并且抵消了已编译编程语言的大多数优点。 But if you think that's the way to go, here's how you'd do it. 但是如果你认为这是要走的路,那么你就是这样做的。 Supposing your Admonish method signatures look like this: 假设您的Admonish方法签名如下所示:

public static void Admonish(this IEnumerable<Naughty> followers)

... and assuming you have a list whose actual implementation type implements IEnumerable<Naughty> : ...并假设您有一个列表,其实际实现类型实现IEnumerable<Naughty>

IList list = new List<Naughty>(); // List<Naughty> implements IEnumerable<Naughty>

... you could find and call the method like this: ...你可以找到并调用这样的方法:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM