簡體   English   中英

如何在運行時將類型未知的對象強制轉換為類型?

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

解決這個問題的類似線程可能在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邏輯,那么可以做任何事情嗎? 很抱歉這篇冗長的文章,只需要解釋我的要求..

更新: StriplingWarrior的答案必然解決我的問題,但我想知道如果我要實現最后一個函數,對象是如何轉換為其原始類型的。 可能通過反思或其他東西。 我將解決方案標記為答案。 再次感謝@StriplingWarrior。

更新2:更清楚地說,忘記上面已發布的所有內容。 假設我有如下功能。 Admonish()是我分別在類型List<Naughty>List<Obedient>對象上定義的擴展方法。 我怎樣才能只編寫一個Sanitize函數並將List<NaughtyList<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();
}

希望問題很清楚。 我不是在尋找特殊情況的解決方案,而是在運行時獲取類型的通用解決方案, 以便可以將對象轉換為它的類型

由於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方法。

對Update 2的回應

這個問題比乍看起來要復雜得多,答案取決於您的告誡方法。 它只是迭代列表並在列表的每個成員上執行特定的操作嗎?

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關鍵字來完成或多或少的嘗試,但它不適用於擴展方法:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM