通过添加依赖于其他属性的属性来构建对象

本文关键字:属性 构建 对象 其他 添加 依赖于 | 更新日期: 2023-09-27 17:51:08

我经常在linq中尝试创建一个对象并为其添加属性而感到沮丧。

的例子:

     var blgFiles = Directory.GetFiles( rootFolder, "*.blg", SearchOption.AllDirectories );
     var data = blgFiles.Select( file => new
     {
        BLGFile = file,
        CSVFile = Path.ChangeExtension( file, "csv" ),
        CSVFileExists = File.Exists( CSVFile )
     } );

无法编译,因为我无法在File.Exists中使用CSVFile .

我可以这样做:

CSVFileExists = File.Exists( Path.ChangeExtension( file, "csv" ) )

但那是多余的。

我也可以做一个恼人的多重选择,我手动复制属性,然后添加新的:

     var data = blgFiles.Select( file => new
     {
        BLGFile = file,
        CSVFile = Path.ChangeExtension( file, "csv" )
     } );
     var data2 = data.Select( file => new
     {
        BLGFile = file.BLGFile,
        CSVFile = file.CSVFile,
        CSVFileExists = File.Exists( file.CSVFile )
     } );

我只是想知道……还有别的办法吗?我正在寻找可以快速添加属性和创建对象的方法,其中一些属性依赖于先前属性的值。

通过添加依赖于其他属性的属性来构建对象

你无能为力。另一个选择:

 var data = blgFiles.Select( file => 
 {
   csvFile = Path.ChangeExtension( file, "csv" );
   return new
   {
      BLGFile = file,
      CSVFile = csvFile ,
      CSVFileExists = File.Exists( csvFile  )
   }
 });

仍然有额外花括号的开销,并且需要显式的返回语句,这使得在这种形式和简化形式之间进行切换有点麻烦。(除非你使用Resharper,它可以帮你完成)

您可以"添加属性并创建对象,其中一些属性依赖于先前属性的值",只需要有"先前"属性。对象初始化器将所有属性设置为一条语句,这就是它的思想。

然而,您可以在select的lambda表达式中创建一个匿名方法来返回您想要的对象。在里面,你可以编写任意的c#代码,这将允许你有多个语句,所以你将有"以前"的语句。

编辑:示例代码已经作为答案

这可能不是您的问题的最佳示例,因为实际上您应该生成两个文件列表,并将它们连接起来,如:

 var blgFiles = Directory.GetFiles( rootFolder, "*.blg", SearchOption.AllDirectories );
 var csvFiles = Directory.GetFiles( rootFolder, "*.csv", SearchOption.AllDirectories );
 var data = blgFiles.GroupJoin(csvFiles,
   k=>Path.GetFilenameWithoutExtension(k),
   v=>Path.GetFilenameWithoutExtension(v),
   (k,g)=>g.Select(v=>new {BLGFILE=k,CSVFILE=v,CSVFileExists=true})
           .DefaultIfEmpty(new {BLGFILE=k,CSVFILE=Path.ChangeExtension(k,"csv"),CSVFile=false})
 ).SelectMany(g=>g);

或者使用单个目录查找:

var files=Directory.EnumerateFiles(rootFolder, "*.*", SearchOption.AllDirectories)
        .Where(s => s.EndsWith(".blg") || s.EndsWith(".csv"));

然后将它们分组,并生成最终输出。这可能是最快的解决方案,因为磁盘I/O相对昂贵,并且只执行一次这部分可能比其他任何考虑都重要。

当然,你总是可以这样做,但我怀疑你是在试图避免它:

 var blgFiles = Directory.GetFiles( rootFolder, "*.blg", SearchOption.AllDirectories );
 var data = blgFiles.Select( file => new
 {
    BLGFile = file,
    CSVFile = Path.ChangeExtension( file, "csv" ),
    CSVFileExists = File.Exists(Path.ChangeExtension( file, "csv" ))
 } );

但是这会产生大量额外的磁盘I/O,并调用ChangeExtension两次。一个更简单的版本也可以使用,但存在同样的缺陷:

var data = blgFiles.Select( file => new
     {
        BLGFile = file,
        CSVFile = Path.ChangeExtension( file, "csv" )
     } ).Select( file => new
     {
        BLGFile = file.BLGFile,
        CSVFile = file.CSVFile,
        CSVFileExists = File.Exists( file.CSVFile )
     } );