高效优雅的代码(翻译)
高效优雅的代码
我仍然很忙,主要是在Revit API讨论论坛中。这里有两个最近的样本处理非常通用的效率相关问题:
简单代码中的效率与优雅
在最近的一项课题的Revit API论坛询问与简单的代码效率和优雅的帮助:
问题:我的代码有效,但我认为它没有那么高效。具体来说,代码的Try / catch区域。如果我不单独尝试/捕捉每条线,它会错过一些门。如果可能的话,也喜欢减少再生时间。任何指针都表示赞赏。谢谢。
1 // Get doors 2 FilteredElementCollector collector = new FilteredElementCollector( doc ); 3 List<Element> coll = collector.OfClass( typeof( FamilyInstance ) ) 4 .OfCategory( BuiltInCategory.OST_Doors ) 5 .ToList(); 6 7 // Filtered element collector is iterable 8 foreach( Element e in coll ) 9 { 10 // Get the parameter name 11 Parameter s_parameter = e.LookupParameter( "Swing Angle" ); 12 Parameter s1_parameter = e.LookupParameter( "Swing Angle_Door 1" ); 13 Parameter s2_parameter = e.LookupParameter( "Swing Angle_Door 2" ); 14 using( Transaction t = new Transaction( doc, "parameters" ) ) 15 16 // Modify document within a transaction 17 using( Transaction tx = new Transaction( doc ) ) 18 { 19 tx.Start( "Change door swing angles to 45" ); 20 21 try 22 { 23 s_parameter.Set( 0.785398163 ); 24 } 25 catch { } 26 try 27 { 28 s1_parameter.Set( 0.785398163 ); 29 } 30 catch { } 31 try 32 { 33 s2_parameter.Set( 0.785398163 ); 34 } 35 catch { } 36 tx.Commit(); 37 } 38 } 39 TaskDialog.Show( "Completed", "Door swings changed to 45°." ); 40 return Result.Succeeded;
通过回答MarryTookMyCoffe:
- 更改
List到Array; 你不必改变foreach到for,因为编译器通常会为你,但请记住,没有的代码优化,for比速度更快foreach。 - 删除第一个
1 using (Transaction t = new Transaction(doc, "parameters"))
- 为什么会这样呢?你把
using交易放在using交易中; 为什么? - 将循环放在事务中,而不是在外部(每次启动和提交都需要花费很多时间)。
- 如果使用string属性获取参数,则必须检查参数是否为null:
1 if(s_parameter != null)
- 检查参数是否为只读,以及是否为double值。
- 因此,应用程序只能返回
Succeeded。 - 你在改变类型或实例吗?您可以添加
WhereElementIsElementType到已过滤的元素收集器。
试试这个:
1 [Transaction( TransactionMode.Manual )] 2 public class Command : IExternalCommand 3 { 4 private const double angle = 0.785398163; 5 public Result Execute( 6 ExternalCommandData commandData, 7 ref string message, 8 ElementSet elements ) 9 { 10 Document doc = commandData.Application.ActiveUIDocument.Document; 11 // Get instance of doors 12 Element[] coll = new FilteredElementCollector( doc ) 13 .OfClass( typeof( FamilyInstance ) ) 14 .OfCategory( BuiltInCategory.OST_Doors ) 15 .WhereElementIsNotElementType() 16 .ToArray(); 17 18 // Start transaction outside of loop, that way you only open transaction once 19 using( Transaction tx = new Transaction( doc ) ) 20 { 21 tx.Start( "Change door swing angles to 45" ); 22 foreach( Element e in coll ) 23 { 24 //set Parameter by the name 25 try 26 { 27 SetParameter( e, "Swing Angle", angle ); 28 SetParameter( e, "Swing Angle_Door 1", angle ); 29 SetParameter( e, "Swing Angle_Door 2", angle ); 30 } 31 catch { } 32 } 33 tx.Commit(); 34 } 35 TaskDialog.Show( "Completed", "Door swings changed to 45°." ); 36 return Result.Succeeded; 37 } 38 39 /// <summary> 40 /// set double parameter by value 41 /// </summary> 42 /// <param name="element"></param> 43 /// <param name="Name"></param> 44 /// <param name="value"></param> 45 /// <returns></returns> 46 private static bool SetParameter( Element element, string Name, double value ) 47 { 48 Parameter parameter = element.LookupParameter( Name ); 49 // Preventing exceptions is better than catching it 50 if( parameter != null && !parameter.IsReadOnly && parameter.StorageType == StorageType.Double ) 51 return parameter.Set( value ); 52 return false; 53 } 54 }
回复:谢谢,完美的工作。现在我将逐步完成代码,以便了解您的所作所为。再次感谢!
答案2:我想补充几点:
LookupParameter只会检索给定名称的第一个参数。为了使用它,您必须绝对确定只存在具有给定名称的一个参数。我建议在加载项启动代码中的某处添加一个测试,以确保实际情况如此。
使用其他方法检索参数总是更安全,例如,内置参数枚举值(如果存在),或者只是Parameter定义,您可以预先查找并缓存,例如,在上述代码中确保该元素类型的参数名称确实是唯一的。
没有必要将元素集合转换为数组,即说
1 Element[] coll = new FilteredElementCollector( doc )...ToArray();
你完全可以把它作为一个过滤后的元素收集器,并直接迭代它:
1 FilteredElementCollector coll = new FilteredElementCollector( doc )...; 2 foreach( Element e in coll ) ...
这将节省转换时间和空间,避免不必要的数据重复,参见 讨论:
将墙壁类型推到门上
关于将墙式推入门的问题中出现了一些非常相似的代码:
问题:我已经在这个宏上工作了一段时间,我们正在寻找一种方法来让我们的门时间表包含墙类型(可在Type Mark参数中找到)。我附在我所在的地方。将墙上的实例参数拉到门中的实例参数我没有任何问题,但我对墙上的类型参数没有太大的帮助。我不确定的唯一区域是我是否正确访问了墙上的类型参数:
1 public void WalltoDoor2() 2 { 3 Document doc = this.ActiveUIDocument.Document; 4 5 ElementCategoryFilter hostFilter 6 = new ElementCategoryFilter( 7 BuiltInCategory.OST_Walls ); 8 ElementCategoryFilter hostedFilter 9 = new ElementCategoryFilter( 10 BuiltInCategory.OST_Doors ); 11 12 string parameterName = "Type Mark"; 13 string parameterName2 = "Wall Type"; 14 using( Transaction t = new Transaction( 15 doc, "Set hosted parameters" ) ) 16 { 17 try 18 { 19 t.Start(); 20 foreach( Element host in 21 new FilteredElementCollector( doc ) 22 .WherePasses( hostFilter ) ) 23 { 24 if( host != null ) 25 { 26 Element hostT = host.Document.GetElement( 27 host.GetTypeId() ); 28 Parameter paramHost = hostT.LookupParameter( 29 parameterName ); 30 31 foreach( Element hosted in 32 new FilteredElementCollector( doc ) 33 .WherePasses( hostedFilter ) 34 .OfClass( typeof( FamilyInstance ) ) 35 .Cast<FamilyInstance>() 36 .Where( q => q.Host.Id == host.Id ) ) 37 { 38 if( hosted != null ) 39 { 40 hosted.LookupParameter( parameterName2 ) 41 .Set( paramHost.AsString() ); 42 } 43 } 44 } 45 } 46 t.Commit(); 47 } 48 catch( Exception ) 49 { 50 } 51 } 52 }
答案:使用内置参数检查器,类型标记看起来像是墙的“ALL_MODEL_TYPE_MARK”。
尝试使用:
1 hostT.LookupParameter("ALL_MODEL_TYPE_MARK");
以下是清理代码的另外两个建议:
- 使用
using的交易- 在一个封装事务using的语句自动地处置它,如果需要回滚。 - 不要捕获所有异常 - 永远不会捕获所有异常,只捕获您真正可以处理的异常。

浙公网安备 33010602011771号