【问题标题】:Generate properties programmatically以编程方式生成属性
【发布时间】:2010-02-04 16:18:35
【问题描述】:

我想加载一个属性文件(它是一个 .csv 文件,每行都有一个名称和相关的数值),然后像这样访问这些属性值:FileLoader.PropertyOneFileLoader.PropertyTwo。问题是我不想为每个值编写一个属性,我希望它们是从文件中生成的。所以

public class FileLoader
{
    public int Property1 { get; private set; }
}

不是我要找的。这可能吗?我看不到任何方法,因为显然编译器不知道属性名称。也许类似的东西?

【问题讨论】:

标签: c# properties


【解决方案1】:

在 C# 4.0 中,您可以使用 ExpandoObject,链接包含很好的解释和几个用例,例如:

dynamic contact = new ExpandoObject();
contact.Name = "Patrick Hines";
contact.Phone = "206-555-0144";
contact.Address = new ExpandoObject();
contact.Address.Street = "123 Main St";
contact.Address.City = "Mercer Island";
contact.Address.State = "WA";
contact.Address.Postal = "68402";

虽然 ExpandoObject 的绝妙之处在于动态创建复杂的分层对象,但我想您通常可以使用它,因为它具有闪亮的语法,即使对于简单的动态定义的对象也是如此。

编辑:这是另一个关于 SO 添加有关 ExpandoObject 好处的详细信息的答案,作者是撰写先前链接文章的同一位专栏作家

What are the true benefits of ExpandoObject?

【讨论】:

  • 在我看来,这是说明为什么动态是一个坏主意的案例之一。没有智能感知、没有类型安全、没有编译时检查、运行时异常——我认为代码生成会好很多。
  • 即使在 ExpandoObject 之前,也可以在 vb6 中使用 Dictionary 对象获得类似的结果,尽管 Dictionary 只能保存 Object:MyDict!Address!Street = MyStreetObject 类型的东西。可以在 vb.net 中实现类似的功能,尽管必须具有最后一个对象的命名属性 [例如st for string]:MyDict!Address!City.st = "Mercer Island";这种方法的优缺点可能与 ExpandoObject 大致相同。
  • 如何添加方法?
【解决方案2】:

Code generation 可以通过几种不同的方式完成。

  • Visual Studio 有T4 templates
  • 您可以使用My Generation 等外部工具(它适用于 ORM 代码生成,不确定是否支持 CSV)。
  • 或者推出您自己的代码来读取文件并写出此类(使用.generated.cs 后缀创建)。

【讨论】:

    【解决方案3】:

    如果不让您的“FileLoader”实际重写自己,然后使用反射来访问新创建的属性,那么实际上没有任何方法可以做到这一点。 (如果您在设计/编译时而不是在运行时工作,则完全忽略此答案=)

    你最终可能会做的是拥有类似

    的东西
    public class FileLoader
    {
      // ... Other code for FileLoader goes here
    
      public FileLoader(string propertiesFileNameAndPath)
      {
        // Load values from propertiesFile into _properties / _propertyValues
      }
    
      private _properties = new List<string>();
      private _propertyValues = new Dictionary<string, string>();
    
      public string[] Properties
      {
        // returning as an array stops your _properties being modified
        return _properties.ToArray();
      }
      public void UpdateProperty(string propertyName, string propertyValue)
      {
        if (propertyValues.ContainsKey(propertyName))
        {
          propertyValues[propertyName] = propertyValue;
        }
      }
      public string GetPropertyValue(string propertyValue)
      {
        if (propertyValues.ContainsKey(propertyName))
        {
          return propertyValues[propertyName];
        }
      }
    }
    

    现在你可以这样做了:

    var fileLoader = new FileLoader("path to properties.csv");
    foreach(var property in fileLoader.Properties)
    {
      var propertyValue = fileLoader.GetPropertyValue(property);
    }
    

    当然,您可以将其简化为将其加载到从 FileLoader 上的方法返回的字典中,但上面的代码保留了使用 FileLoader 属性的“外观”的一部分 class=)

    编辑:添加“索引器”代码

    可能使语法更清晰的一件事是使用“索引器”,因此您需要将以下内容添加到 FileLoader:

      public string this[string index]  
      {
        if (propertyValues.ContainsKey(propertyName))
        {
          return propertyValues[propertyName];
        }
        else
        {
          return null;
        }
      }
    

    那么 accessint 的代码会稍微整洁一些:

    var fileLoader = new FileLoader("path to properties.csv");
    foreach(var property in fileLoader.Properties)
    {
      var propertyValue = fileLoader[property];
    }
    

    【讨论】:

    • +1 用于索引器。这就是我在阅读问题时立即想到的。
    【解决方案4】:

    这是一个使用 ExpandoObject 和 C#`s 4.0 动态特性的示例

    public dynamic ParseCsvFile(string filePath) {
      var expando = new ExpandoObject;
      IDictionary<string,object> map = expando;
      foreach ( var line in File.ReadAllLines(filePath)) {
        var array = line.Split(new char[]{','},2);
        map.Add(array[0],array[1]);
      }
      return expando;
    }
    
    ...
    var d = ParseCsvFile(someFilePath);
    Console.WriteLine(d.Property1);
    

    【讨论】:

      【解决方案5】:

      您基本上需要进行一些代码生成。

      编写一个简单的控制台应用程序或 win forms 应用程序,加载 csv,然后从 csv 获取信息并生成 .cs 文件。

      【讨论】:

      • 我认为他希望它们在运行时生成,而不是在编译时生成,尽管通过宏(或脚本或其他)生成代码仍然是一个不错的途径。如果在应用运行时属性发生变化,则可能需要运行时生成。
      • 我认为这与编译时有关,否则拥有真正的属性并且不必手动编写声明就没有意义了。但是。
      【解决方案6】:

      有一个简单的解决方案,将属性读入字典并重载FileLoader 的数组运算符:

      public T this[string propertyName]  
      {  
          get { return yourDictionary[propertyName]; }  
      }
      

      这样,您可以使用fileLoadObject["SomePropertyName"] 访问属性。

      正如 Oded 所指出的,可以使用反射动态添加属性。有一个例子over here

      【讨论】:

      • 反射呢?您可以使用它创建动态对象。
      • 谢谢你的提示,我不知道。
      • @AndiDog - 您可以使用反射在 C# 中创建属性。我在某个地方有一些示例代码,我用过它,只需要挖掘它
      • 好的,我编辑了答案以提供正确的信息。我还添加了一个动态创建属性的教程。
      【解决方案7】:

      这可以通过 c# 4.0 中的新动态内容实现。我不是专家,但是使用动态对象,您可以定义在调用不存在的方法时的行为。 This post 展示了一个非常有趣的示例,说明如何设置一个可以做你想做的事情的动态字典。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-04-27
        • 1970-01-01
        • 2010-09-06
        • 2018-06-23
        • 2018-08-14
        • 1970-01-01
        • 2023-03-14
        • 1970-01-01
        相关资源
        最近更新 更多