我有一个接收Object []然后对该数组执行操作的方法。
首先,我将此数组作为IEnumerable<T>
传递,但T可以是两种不同的类型。
T将始终具有相同的属性,即使它们是不同的类型。
是否可以在运行时转换为类型,以便我可以使用我知道每个属性包含的属性?
所以可以做到的地方:
var dataObject = (IEnumerable<T>) dataArray;
以某种方式可行:
var dataObject = (dataArray.GetType()) dataArray;
答案 0 :(得分:5)
您是否可以修改两种T
类型的源代码?如果是这样,那么你可以让它们两个(a)从公共基类继承,或者(b)实现一个公共接口。
修改以下评论...
为了容纳ID属性的不同类型,您可以使用explicit interface implementation。这将允许您在通过公共接口访问时将第二个对象的ID公开为Int64
。 (当不通过界面访问时,ID属性仍可作为Int32
访问。)
不幸的是,显式接口实现需要对原始类型进行更多更改。
void YourMethod(IEnumerable<ICommonToBothTypes> sequence)
{
foreach (var item in sequence)
{
Console.WriteLine(item.ID);
}
}
// ...
public interface ICommonToBothTypes
{
long ID { get; }
}
public class FirstType : ICommonToBothTypes
{
public long ID
{
get { return long.MaxValue; }
}
}
public class SecondType : ICommonToBothTypes
{
// the real Int32 ID
public int ID
{
get { return int.MaxValue; }
}
// explicit interface implementation to expose ID as an Int64
long ICommonToBothTypes.ID
{
get { return (long)ID; }
}
}
答案 1 :(得分:1)
您可以将其创建为接口,并让这两个类实现接口。然后使用Interface []作为参数而不是Object,并且避免一起转换。
答案 2 :(得分:1)
如果数组中的两个对象派生自相同的基类型(继承),那么您可以将数组中的所有对象作为基类对象,并且可以使用它们而不知道它可能是哪个特定对象。 / p>
请在此处查看Microsoft的Boxing and Unboxing信息
这实质上是polymorphism的用途,处理具有公共接口的对象。
在这种情况下,从同一个基类继承或创建两个对象都支持的接口。然后,您将能够使用基类类型或接口类型专门定义数组,而不是“对象”。
答案 3 :(得分:1)
如果不可能让不同的类都继承相同的接口,则可以将对象中定义的所有属性作为集合,然后通过键查找获取其值,其中键是属性的名称。我将它实现为扩展方法,如下所示:
public static Dictionary<string, object> GetProperties<T>(this T instance)
where T : class
{
var values = new Dictionary<string, object>();
var properties =
instance.GetType().GetProperties(BindingFlags.Public |
BindingFlags.Instance |
BindingFlags.GetProperty);
foreach (var property in properties)
{
var accessors = property.GetAccessors();
if ((accessors == null) || (accessors.Length == 0))
continue;
string key = property.Name;
object value = property.GetValue(instance, null);
values.Add(key, value);
}
return values;
}
然后您可以这样做以获取属性值:
void DoStuff(object[] objects)
{
foreach (var o in objects)
{
var properties = o.GetProperties();
var value = properties["PropertyName"];
}
}
如果您忘记在消费代码中重命名查找键,那么在重命名属性时,它们都将是无类型的并且您将会破坏,否则,这应该可以正常工作。但是,最好的解决方案无疑是Luke建议的(使用接口)。
答案 4 :(得分:1)
如果您有权访问定义两种元素类型的源代码
您应该引入一个包含公共属性的新interace,并让其他两个接口继承您的新基接口。
在您的方法中,然后使用IEnumerable.Cast(TResult)
将所有元素强制转换为公共接口:
var dataObject = dataArray as IEnumerable;
if (dataObject != null)
{
var data = dataObject.Cast<ICommonBase>()
// do something with data
}
如果您无法修改定义两种元素类型的源代码:
使用IEnumerable.OfType()根据已知类型过滤元素,有效地创建两个强类型集合。接下来对每个操作执行相同的操作(最冗长的方法)。
var dataObject = dataArray as IEnumerable;
if (dataObject != null)
{
var a = dataObject.OfType<InterfaceA>()
// do something with a
var b = dataObject.OfType<InterfaceB>()
// do the same with b
}
可以在msdn上找到所用函数的参考。
如果你想避免代码重复,你唯一的机会就是使用一个名为 duck typing 的东西.Phil Haack上有一个great article。 我们的想法是引入一个包含公共元素的新界面。然后,您可以对数组元素进行类型化处理,从而有效地使它们符合运行时的新接口。
最后一个选项是使用通用名称反射和提取属性信息,然后访问每个元素数据。这是最慢和恕我直言最麻烦的方法。