在动态地将新方法,属性添加到现有类中时需要帮助

时间:2010-05-30 06:55:49

标签: c# .net class class-design

我不确定是否有可能在Dot Net中实现这种实现。以下是信息

目前,我们使用的是COM +,ASP,XSL,XML技术。它是一个多层体系结构应用程序,其中COM +充当BAL。将使用单独的UI定义任何CRUD操作的执行步骤,该UI使用XML来存储信息。 BAL读取XML并理解定义的执行步骤并在DLL中执行相应的方法。与EDM非常相似,我们使用自定义模型(使用XML)来确定对象的哪个属性是可搜索的,可检索的等等。基于此信息,BAL构造查询并调用过程来获取数据。

在当前的应用程序中,BAL和DAL都可以在不进行任何代码更改的情况下进行大量自定义。结果将以XML格式传输到表示层,根据收到的数据构建UI。

现在我正在创建一个处理员工信息的迁移项目。它也将遵循N层架构,其中表示层与BAL通信,BAL连接到DAL以返回数据。

问题在于,在我们现有的版本中,我们将每个信息作为XML以其原生形式处理(没有对象的转换等),但是在迁移项目中,Team真的对使用OOP开发模型感兴趣。从BAL发送的信息需要转换为其各自类型的对象(例如employeeCollection,Address Collection等)。

如果我们有从BAL返回的静态数据,我们可以有一个包含这些节点作为属性的类,我们可以访问它们。但在我们的例子中,从我们的BAL返回的数据需要定制。我们如何处理将结果转换为Object的表示层中的自定义。

以下是返回的XML的示例

<employees>
    <employee>
        <firstName>Employee 1 First Name</firstName>
        <lastName>Employee 1 Last Name</lastName>
        <addresses>
            <address>
                <addressType>1</addressType>
                <StreetName>Street name1</StreetName>
                <RegionName>Region name</RegionName>
            <address>
            <address>
                <addressType>2</addressType>
                <StreetName>Street name2</StreetName>
                <RegionName>Region name</RegionName>
            <address>
            <address>
                <addressType>3</addressType>
                <StreetName>Street name3</StreetName>
                <RegionName>Region name</RegionName>
            <address>
        <addresses>
    </employee>
    <employee>
        <firstName>Employee 2 First Name</firstName>
        <lastName>Employee 2 Last Name</lastName>
        <addresses>
            <address>
                <addressType>1</addressType>
                <StreetName>Street name1</StreetName>
                <RegionName>Region name</RegionName>
            <address>
            <address>
                <addressType>2</addressType>
                <StreetName>Street name2</StreetName>
                <RegionName>Region name</RegionName>
            <address>
        <addresses>
    </employee>
</employees>

如果这些是唯一的列,那么我可以写一个类似

的类
public class Address{
    public int AddressType {get;set;};
    public string StreetName {get;set;};
    public string RegionName {get;set;};
}

public class Employee{
    public string  FirstName {get; set;}
    public string  LastName {get; set;}
    public string  AddressCollection {get; set;}
}

public class EmployeeCollection : List<Employee>{
    public bool Add (Employee Data){
    ....
    }
}

public class AddressCollection : List<Address>{
    public bool Add (Address Data){
    ....
    }
}

此类将作为DLL提供给客户和顾问。我们不会提供相同的源代码。

现在,当顾问或客户进行自定义时(例如添加国家/地区以及使用员工对象添加护照信息对象),他们必须能够访问这些类中的这些属性,但如果没有源代码,他们将无法执行这些操作modification.which使应用程序无用。有没有办法在DotNet中实现这一点。

我想过使用匿名类,但是匿名类的问题是

我们不能有方法。 我不知道我怎么能适应集合对象(这将是一个匿名类) 不确定datagrid /用户控件绑定等。

我还想过使用CODEDom创建类运行时但不确定性能问题。类也必须只创建一次,并且必须使用相同的东西,直到有另一个变化。

请帮我解决这个问题。任何类型的帮助材料/神秘代码/链接都会有所帮助。

更新

感谢您提供的答案让我了解了我没想过的不同领域。

我在问题中遗漏了一个信息。 UI代码也将作为DLL而不是源代码。客户可以自由创建新代码并附加到UI,但他们无法通过代码更改现有UI。我们有一个设计师,它将显示可选择的可显示属性,客户可以从中选择向用户显示数据。这样他们就可以改变UI的外观。他们也可以使用这个模型更改绑定属性。

如果我在这个问题中给出了这篇文章,我可能会得到更多答案,并且可能会让你清楚地知道究竟发生了什么。

上面的内容,下面是我的新想法

我也在考虑使用CodeDom模型单独创建类并将其迁移到UI。以下是我的想法

创建一个控制台应用程序,它读取我们结构的XML文件。它将包含系统中可用的属性和类。还要创建一个UI来操作XML文件来创建类。在控制台中使用COdeDom生成类和方法。使用库或shell命令编译它们。将创建的类移动到UI,现在UI将重新定义新类并显示属性。

这可能不会造成任何内存占用和高内存利用率/泄漏。我们也正在替换简单的DLL。唯一的问题是班级生成。它必须以团队必须能够在此UI中执行任何操作的方式进行复杂化。你们怎么想?

3 个答案:

答案 0 :(得分:1)

如果您允许它们从您的基类继承新的Address和Employee类,那么应该这样做。

public class Employee
{
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public string AddressCollection { get; set; }
}

public class EmployeeCollection<T> : List<T> where T : Employee
{
  public bool Add(T Data)
  {
    return this.Add(Data);
  }
}

然后派生员工会看起来像这样

public class EmployeePlus : Employee
{
  public string PassportNo { get; set; }
}

如果您希望stronly类型的集合是EmployeePlus,您可以简单地定义您的集合,如果您需要...

public class EmployeeCollection<T> : List<T> where T : Employee
{
  public bool Add(T Data)
  {
    return this.Add(Data);
  }
}

现在可以使用强类型的Employee和派生对象集合,如下所示

EmployeeCollection<EmployeePlus> employees = 
  new EmployeeCollection<EmployeePlus>();

答案 1 :(得分:0)

这似乎是一个奇怪的设置,但有很多方法可以 做你想做的事情:

  • 类继承(可能正在制作 你的基类是抽象的但是 部分类是另一种选择)
  • 使用接口而不是类并让您的客户 担心实施
  • 常用的扩展方法(ref1ref2) 扩展第三方代码,但不是 通常期望使用。
  • decorators

通常情况下,我希望在你的场景中使用Interfaces,但是如果你也试图混合行为,我会冒险猜测重构可能在这一点上太痛苦和类继承是你最终会去的地方。

答案 2 :(得分:0)

尝试使用MSIL

public class RunTimeObject<T> where T : class
{

    void EmitGetter(MethodBuilder methodBuilder, FieldBuilder fieldBuilder)
    {
        ILGenerator ilGenerator = methodBuilder.GetILGenerator();
        ilGenerator.Emit(OpCodes.Ldarg_0);
        ilGenerator.Emit(OpCodes.Ldfld, fieldBuilder);
        ilGenerator.Emit(OpCodes.Ret);
    }

    void EmitSetter(MethodBuilder methodBuilder, FieldBuilder fieldBuilder)
    {
        ILGenerator ilGenerator = methodBuilder.GetILGenerator();
        ilGenerator.Emit(OpCodes.Ldarg_0);
        ilGenerator.Emit(OpCodes.Ldarg_1);
        ilGenerator.Emit(OpCodes.Stfld, fieldBuilder);
        ilGenerator.Emit(OpCodes.Ret);
    }

    public object CreateNewObject(T obj)
    {

        AssemblyName assemblyName = new AssemblyName { Name = "assembly" };
        AssemblyBuilder assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
        ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("module");
        TypeBuilder typeBuilder = moduleBuilder.DefineType("DynamicType", TypeAttributes.Public | TypeAttributes.Class);

        foreach (var prop in obj.GetType().GetProperties())
        {
            FieldBuilder field = typeBuilder.DefineField("_" + prop.Name, typeof(string), FieldAttributes.Private);

            PropertyBuilder propertyBuilder =
                typeBuilder.DefineProperty(prop.Name,
                                 PropertyAttributes.None,
                                 typeof(string),
                                 new Type[] { typeof(string) });

            MethodAttributes methodAttributes =
                MethodAttributes.Public |
                MethodAttributes.HideBySig;

            MethodBuilder methodBuilderGetter =
                typeBuilder.DefineMethod("get_value",
                                           methodAttributes,
                                           typeof(string),
                                           Type.EmptyTypes);

            EmitGetter(methodBuilderGetter, field);


            MethodBuilder methodBuilderSetter =
                typeBuilder.DefineMethod("set_value",
                                           methodAttributes,
                                           null,
                                           new Type[] { typeof(string) });

            EmitSetter(methodBuilderSetter, field);


            propertyBuilder.SetGetMethod(methodBuilderGetter);
            propertyBuilder.SetSetMethod(methodBuilderSetter);
        }

        Type dynamicType = typeBuilder.CreateType();

        var dynamicObject = Activator.CreateInstance(dynamicType);

        var properties = dynamicType.GetProperties();

        int count = 0;

        foreach (var item in obj.GetType().GetProperties())
            properties[count++].SetValue(dynamicObject, item.GetValue(obj, null), null);

        return dynamicObject;

    }
}