65.9K
CodeProject 正在变化。 阅读更多。
Home

C# 对象到接口转换器实用程序

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (20投票s)

2009年4月25日

CPOL

3分钟阅读

viewsIcon

62203

downloadIcon

560

一个将对象转换为接口的实用程序,即使它没有正式实现该接口。

引言

在本文中,我描述了一种独特的方法,可以将任何对象转换为它实际公开的接口,即使它没有正式实现特定的接口。 为了了解我在这里要提出的内容,请考虑以下示例。

假设您正在使用处理人员的第三方库(LibA 和 LibB)。 这些库定义了 PersonLibAPersonLibB 类,并公开了这些人员的实例。 它们很可能公开一个类似的公共接口,并且具有 NameEMailAddressDateOfBirth 等属性。 我几乎可以肯定您希望统一访问这些人。 如果您有这些组件的源代码,您可以定义一个公共接口并从您新定义的接口实现 PersonLibAPersonLibB 类。 但是,如果源代码不可用,则该方法将不起作用。 唯一要做的就是为每个人员类定义包装类并实现公共接口。

同样,如果您想统一访问 WPF TextBoxTextBlockText 属性,您很可能需要定义两个包装类来公开 Text 属性。

如果涉及的第三方类不多,并且公共接口足够简单,则可以轻松完成。 否则,这个过程将非常痛苦。 我在这里建议的是一个实用程序类,它通过动态生成包装类并在幕后为您完成所有脏活来将对象转换为接口。 我将直接进入用法,稍后将介绍解决方案中使用的一些技巧。

使用代码

该实用程序作为模板类,其中 T 是要转换到的接口。 它有一个公共方法 T As(object o),它获取任何类型的对象,如果成功则返回转换后的包装器对象。

public static class ObjectCaster<T>
{
    public static T As(object o)
    {
        // ....
    }
}

考虑以下 Person 类。 请注意,它没有实现任何接口。

public class Person
{
    public string Name { get; set; }

    public event EventHandler Initialized;

    public void Initialize()
    {
        Name = "Initialized";
        EventHandler e = Initialized;
        if (e != null)
            e(this, EventArgs.Empty);
    }
}

但是,它与以下接口完全对齐

public interface IPerson
{
    string Name { get; set; }

    event EventHandler Initialized;

    void Initialize();
}

以下是实际的演示代码,它使用 ObjectCaster 类并将 Person 类的实例转换为 IPerson 接口(第 4 行)。

Person obj = new Person() { Name = "John" };
Console.WriteLine(string.Format("(obj is IPerson) == {0}", obj is IPerson));

IPerson iperson = ObjectCaster<IPerson>.As(obj);
iperson.Initialized += ((sender, e) => Console.WriteLine("Initialized Called"));

Console.WriteLine("Person> " + obj.Name);
Console.WriteLine("IPerson> " + iperson.Name);

iperson.Name = "Steve";

Console.WriteLine("Person> " + obj.Name);
Console.WriteLine("IPerson> " + iperson.Name);

iperson.Initialize();

Console.WriteLine("Person> " + obj.Name);
Console.WriteLine("IPerson> " + iperson.Name);

正如您在上面代码的输出中看到的那样,您会很容易注意到该对象没有正式实现 IPerson 接口,并且正确地包装了属性、方法和事件。 您可能还会注意到,包装的事件处理程序将原始发送者(它是 Person 的实例)的值修改为转换后的包装器对象(iperson 变量)的实例。

binary_CSharp_Object_Caster

背景

该解决方案的关键点集中在 GenerateProxyClass 方法中

private static CodeCompileUnit GenerateProxyClass(Type interfaceType, Type sourceType)
{
    //...
}

基本上,它使用 System.CodeDom 机制生成一个新的代理类,该类包装 sourceType 并实现 interfaceType 接口。 借助 .NET 反射,它遍历 interfaceType 类型的成员,并在代理类中生成相应的成员。 之后,它使用 CodeDomProvider 类和调用 CompileAssemblyFromDom 方法来编译生成的类。 此时,生成的类已编译,最后要做的是创建一个新的代理类实例并提供源对象作为构造函数。 实用程序类和演示应用程序的完整源代码可在上面的下载部分找到。

未来工作

关于此主题要做的下一件事是提供重新映射成员命名和添加类型转换的功能。 即使成员的名称和类型在接口和源类中不相同,它也将允许转换为接口。

Origin

本文的来源可以在这里找到:C# 对象到接口转换实用程序

历史

  • 2009 年 4 月 26 日 - 修复了一个错误,以支持非 GAC 汇编。 感谢 Oleg Shilo
  • 2009 年 4 月 25 日 - 初始版本。
© . All rights reserved.