未指定的通用属性的比较

时间:2017-07-25 10:05:03

标签: c# generics

请考虑以下代码:

public interface IIdentifiable<T>
{
    T Id { get; set; }
}

public interface IViewModel
{
}

public class MyViewModel1 : IViewModel, IIdentifiable<int>
{
    public string MyProperty { get; set; }

    public int Id { get; set; }
}

public class MyViewModel2 : IViewModel, IIdentifiable<string>
{
    public string MyProperty { get; set; }

    public string Id { get; set; }
}

我也有使用ViewModels运行的类:

public class Loader<T> where T: IViewModel
{
    public void LoadData()
    {
        /*some important stuff here*/

        if (typeof(IIdentifiable<??>).IsAssignableFrom(typeof(T)))
        {                     // ^- here's the first problem
            data = data.Where(d => _dataSource.All(ds => ((IIdentifiable<??>) ds).Id != ((IIdentifiable<??>) d).Id)).ToList();
        }                                                             // ^---- and there the second ----^

        /*some important stuff here too*/
    }
}

现在,正如您所看到的,我拥有的viewmodel可能会实现IIdentifiable<>接口。我想检查一下,如果它是真的, 我想确保我的data列表中不包含我_dataSourse列表中已存在的任何条目。

所以我有两个问题:

  1. 我不知道IIdentifiable<>在其通用括号中的含义,可能是intstring甚至GUID。 我尝试typeof(IIdentifiable<>).IsAssignableFrom(typeof(T))这是正确的语法,但它总是返回false。 有没有办法在不知道确切的泛型类型的情况下检查T是否为IIdentifiable<>

  2. 如果对第一个问题有答案,我还想知道如何在不知道其类型的情况下比较Id字段。 我发现this answer非常有用,但它并没有覆盖我的 具体案例。

  3. 我知道如果我将Loader<T>类设为两种类型Loader<T,K>的通用类型,我可能可以解决该问题,其中K将是 输入IIdentifiable<>,但我想知道是否有其他解决方案。

    P.S。除了我的第一个问题:我还很好奇为什么如果在未指定通用类型typeof(IIdentifiable<>).IsAssignableFrom(typeof(T))时它返回false,那么可以编写类似IIdentifiable<>的内容?

    编辑:我想,事后看来,我明白为什么我不能直截了当地编写代码 - 因为可能是集合ICollection<IViewModel>条目实现了不同类型的IIdentifiable<>(或根本不实现它),这样的检查会笨拙地失败。然而,也许有一种方法可以做一些限制,但没有为我的Loader创建第二个通用参数?

3 个答案:

答案 0 :(得分:1)

尝试向Loader<T>添加两种方法:

public bool CanCast<TId>()
{
    var identifiableT = typeof(IIdentifiable<>).MakeGenericType(typeof(TId));
    return identifiableT.IsAssignableFrom(typeof(T));
}

public IEnumerable<IIdentifiable<TId>> Filter<TId>(IEnumerable<T> data)
{
    return data.Where(d => _dataSource.All(
      ds => !((IIdentifiable<TId>) ds).Id.Equals(((IIdentifiable<TId>) d).Id)));
}

然后在LoadData

if (CanCast<int>())
    data = Filter<int>(data);
else if (CanCast<Guid>())
    data = Filter<Guid>(data);
// and so om

答案 1 :(得分:1)

好吧,我建议你总是用字符串进行识别。您可以将int和guid转换为字符串。如果您想确保使用正确的类型,那么您可以在字符串前面加上类型信息。

但是,我确实认为你算法的性能会很差,因为你基本上是循环2个容器所以它会是O(n * m)。

因此,如果两个源都来自数据库,那么最好是执行适当的SQL查询,如果在代码中执行,则使用字典。或者,如果数据正确排序,您可以更有效地找到重复项。

顺便说一句,泛型在C#中非常有限。有时候使用'Func&lt;&gt;'会有所帮助,但即使这样你也必须为算法提供额外的信息。

答案 2 :(得分:1)

我们应该分两步解决你的问题(因为这里有两个问题需要解决)。

首先,对您的界面IIdentifiable<T>

进行以下更改
public interface IIdentifiable<T>
    where T : IEquatable<T>
{
    T Id { get; set; }
}

这将确保您可以正确比较Id属性。

其次,在您的LoadData()方法中,将if语句更改为

    if (T is IIdentifiable<T>)
    {                     // ^- here's the first problem
        data = data.Where(d => _dataSource.All(ds => ((IIdentifiable<T) ds).Id != ((IIdentifiable<T) d).Id)).ToList();
    }