请考虑以下代码:
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
列表中已存在的任何条目。
所以我有两个问题:
我不知道IIdentifiable<>
在其通用括号中的含义,可能是int
,string
甚至GUID
。
我尝试typeof(IIdentifiable<>).IsAssignableFrom(typeof(T))
这是正确的语法,但它总是返回false
。
有没有办法在不知道确切的泛型类型的情况下检查T
是否为IIdentifiable<>
?
如果对第一个问题有答案,我还想知道如何在不知道其类型的情况下比较Id
字段。
我发现this answer非常有用,但它并没有覆盖我的
具体案例。
我知道如果我将Loader<T>
类设为两种类型Loader<T,K>
的通用类型,我可能可以解决该问题,其中K
将是
输入IIdentifiable<>
,但我想知道是否有其他解决方案。
P.S。除了我的第一个问题:我还很好奇为什么如果在未指定通用类型typeof(IIdentifiable<>).IsAssignableFrom(typeof(T))
时它返回false,那么可以编写类似IIdentifiable<>
的内容?
编辑:我想,事后看来,我明白为什么我不能直截了当地编写代码 - 因为可能是集合ICollection<IViewModel>
条目实现了不同类型的IIdentifiable<>
(或根本不实现它),这样的检查会笨拙地失败。然而,也许有一种方法可以做一些限制,但没有为我的Loader
创建第二个通用参数?
答案 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();
}