将通用对象数组转换为两种类型

时间:2009-11-09 16:41:02

标签: c# casting

我有一个接收Object []然后对该数组执行操作的方法。

首先,我将此数组作为IEnumerable<T>传递,但T可以是两种不同的类型。

T将始终具有相同的属性,即使它们是不同的类型。

是否可以在运行时转换为类型,以便我可以使用我知道每个属性包含的属性?

所以可以做到的地方:

var dataObject = (IEnumerable<T>) dataArray;

以某种方式可行:

var dataObject = (dataArray.GetType()) dataArray;

5 个答案:

答案 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。 我们的想法是引入一个包含公共元素的新界面。然后,您可以对数组元素进行类型化处理,从而有效地使它们符合运行时的新接口

最后一个选项是使用通用名称反射和提取属性信息,然后访问每个元素数据。这是最慢和恕我直言最麻烦的方法。