[原创]深入理解C# 3.x的新特性(2):Extension Method - Part II

本文详细解析了 C# 中 ExtensionMethod 的工作原理及其本质,通过 IL 代码对比 StaticMethod 和 ExtensionMethod,并通过一个完整的示例展示了如何自定义 LINQ Operator,最后介绍了 LINQ 的 Deferred Evaluation 特性。

四、Extension Method的本质

通过上面一节的介绍,我们知道了在C#中如何去定义一个Extension Method:它是定义在一个Static class中的、第一个Parameter标记为this关键字的Static Method。在这一节中,我们来进一步认识Extension Method。

和C# 3.0的其他新特性相似,Extension Method仅仅是C#这种.NET Programming Language的新特性而已。我们知道,C#是一种典型的编译型的语言,我们编写的Source Code必须先经过和C# Compiler编译成Assembly,才能被CLR加载,被JIT 编译成Machine Instruction并最终被执行。C# 3.0的这些新的特性大都影响Source被C# Compiler编译成Assembly这个阶段,换句话说,这些新特仅仅是Compiler的新特性而已。通过对Compiler进行修正,促使他将C# 3.0引入的新的语法编译成相对应的IL Code,从本质上看,这些IL Code 和原来的IL并没有本质的区别。所有当被编译生成成Assembly被CLR加载、执行的时候,CLR是意识不到这些新的特性的。

从Extension Method的定义我们可看出,Extension Method本质上是一个Static Method。但是我们往往以Instance Method的方式进行调用。C# Compiler的作用很明显:把一个以Instance Method方式调用的Source Code编译成的于对应于传统的Static Method调用的IL Code

虽然Extension Method本质上仅仅是一个Static Class的Static Method成员,但是毕竟和传统的Static Method有所不同:在第一个Parameter前加了一个this关键字。我们现在来看看他们之间的细微的差异。我们先定义一个一般的Static Method:

public   static  Vector Adds(Vector v, Vector v1)
{
  
return   new  Vector { X  =  v.X  +  v1.X, Y  =  v.Y  +  v1.Y };
}

注:Vector的定义参见《深入理解C# 3.0的新特性(2):Extension Method - Part I》。

我们来看看通过Compiler进行编译生成的IL:

.method  private  hidebysig  static   void   Main( string [] args) cil managed
{
  .entrypoint
  
//  Code size       50 (0x32)
  .maxstack   2
  .locals init ([
0 class  Artech.ExtensionMethod.Vector v,
           [
1 class  Artech.ExtensionMethod.Vector  ' <>g__initLocal0 ' )
  IL_0000:  nop
  IL_0001:  newobj     instance 
void  Artech.ExtensionMethod.Vector::.ctor()
  IL_0006:  stloc.
1
  IL_0007:  ldloc.
1
  IL_0008:  ldc.r8     
1 .
  IL_0011:  callvirt   instance 
void  Artech.ExtensionMethod.Vector::set_X(float64)
  IL_0016:  nop
  IL_0017:  ldloc.
1
  IL_0018:  ldc.r8     
2 .
  IL_0021:  callvirt   instance 
void  Artech.ExtensionMethod.Vector::set_Y(float64)
  IL_0026:  nop
  IL_0027:  ldloc.
1
  IL_0028:  stloc.
0
  IL_0029:  ldloc.
0
  IL_002a:  ldloc.
0
  IL_002b:  call       
class  Artech.ExtensionMethod.Vector Artech.ExtensionMethod.Extension::Adds( class  Artech.ExtensionMethod.Vector,
class  Artech.ExtensionMethod.Vector)
  IL_0030:  stloc.
0
  IL_0031:  ret
//  end of method Program::Main

对了解IL的人来说,对上面的IL code应该很容易理解。

我们再来看看对于通过下面的方式定义的Extension Method:

public   static   class  Extension
    
{
         
public static Vector Adds(this Vector v, Vector v1)
        
{
            
return new Vector { X = v.X + v1.X, Y = v.Y + v1.Y };
        }

}

对于得IL如下:

.method  public  hidebysig  static   class  Artech.ExtensionMethod.Vector 
Adds(
class  Artech.ExtensionMethod.Vector v,
class  Artech.ExtensionMethod.Vector v1) cil managed
{
  .custom instance 
void [System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) 
  
// Code size       53 (0x35)
  .maxstack  3
  .locals init ([
0class Artech.ExtensionMethod.Vector '<>g__initLocal0',
           [
1class Artech.ExtensionMethod.Vector CS$1$0000)
  IL_0000:  nop
  IL_0001:  newobj     instance 
void Artech.ExtensionMethod.Vector::.ctor()
  IL_0006:  stloc.
0
  IL_0007:  ldloc.
0
  IL_0008:  ldarg.
0
  IL_0009:  callvirt   instance float64 Artech.ExtensionMethod.Vector::get_X()
  IL_000e:  ldarg.
1
  IL_000f:  callvirt   instance float64 Artech.ExtensionMethod.Vector::get_X()
  IL_0014:  add
  IL_0015:  callvirt   instance 
void Artech.ExtensionMethod.Vector::set_X(float64)
  IL_001a:  nop
  IL_001b:  ldloc.
0
  IL_001c:  ldarg.
0
  IL_001d:  callvirt   instance float64 Artech.ExtensionMethod.Vector::get_Y()
  IL_0022:  ldarg.
1
  IL_0023:  callvirt   instance float64 Artech.ExtensionMethod.Vector::get_Y()
  IL_0028:  add
  IL_0029:  callvirt   instance 
void Artech.ExtensionMethod.Vector::set_Y(float64)
  IL_002e:  nop
  IL_002f:  ldloc.
0
  IL_0030:  stloc.
1
  IL_0031:  br.s       IL_0033
  IL_0033:  ldloc.
1
  IL_0034:  ret
}
  //  end of method Extension::Adds

通过比较,我们发现和上面定义的一般的Static Method生成的IL唯一的区别就是:在Adds方法定义最开始添加了下面一段代码:

.custom instance  void  [System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor()  =  (  01   00   00   00  ) 

这段添加的IL代码很明显,就是在Adds方法上添加一个Customer Attribute:System.Runtime.CompilerServices.ExtensionAttribute。ExtensionAttribute具有如下的定义:

[AttributeUsage(AttributeTargets.Method  |  AttributeTargets.Class  |  AttributeTargets.Assembly)]
public   sealed   class  ExtensionAttribute : Attribute
{
}

所以下面Extension Method的定义

public   static  Vector Adds( this  Vector v, Vector v1)
{
            
return new Vector { X = v.X + v1.X, Y = v.Y + v1.Y };
}

和下面的定义是等效的

[ExtensionAttribute]
public   static  Vector Adds(Vector v, Vector v1) 
{
            
return new Vector { X = v.X + v1.X, Y = v.Y + v1.Y };
}

但是,System.Runtime.CompilerServices.ExtensionAttribute和其他Custom Attribute不一样,因为它是为了Extension Method的而定义的,我们只能通过添加this Key word的语法来定义Extension Method。所以当我们将System.Runtime.CompilerServices.ExtensionAttribute直接运用到Adds方法会出现下面的Compile Error:

Do not use  ' System.Runtime.CompilerServices.ExtensionAttribute ' . Use the  ' this '  keyword instead.

上面我们比较了Extension Method本身IL和一般Static Method IL,现在我们看看当我们以Instance Method方式调用Extension Method的IL。假设我们通过下面的方式调用Adds。 

class  Program
    
{
        
static void Main(string[] args)
        
{
 var v 
= new Vector { X = 1, Y = 2 };
           v 
= v.Adds(v);
        }

}

下面是Main Method的IL:

.method  private  hidebysig  static   void   Main( string [] args) cil managed
{
  .entrypoint
  
// Code size       50 (0x32)
  .maxstack  2
  .locals init ([
0class Artech.ExtensionMethod.Vector v,
           [
1class Artech.ExtensionMethod.Vector '<>g__initLocal0')
  IL_0000:  nop
  IL_0001:  newobj     instance 
void Artech.ExtensionMethod.Vector::.ctor()
  IL_0006:  stloc.
1
  IL_0007:  ldloc.
1
  IL_0008:  ldc.r8     
1.
  IL_0011:  callvirt   instance 
void Artech.ExtensionMethod.Vector::set_X(float64)
  IL_0016:  nop
  IL_0017:  ldloc.
1
  IL_0018:  ldc.r8     
2.
  IL_0021:  callvirt   instance 
void Artech.ExtensionMethod.Vector::set_Y(float64)
  IL_0026:  nop
  IL_0027:  ldloc.
1
  IL_0028:  stloc.
0
  IL_0029:  ldloc.
0
  IL_002a:  ldloc.
0
  IL_002b:  call       
class Artech.ExtensionMethod.Vector Artech.ExtensionMethod.Extension::Adds(class Artech.ExtensionMethod.Vector,
class Artech.ExtensionMethod.Vector)
  IL_0030:  stloc.
0
  IL_0031:  ret
}
  //  end of method Program::Main

通过上面的IL,我们看到调用的是Artech.ExtensionMethod.Extension的Adds方法。

IL_002b:  call  class  Artech.ExtensionMethod.Vector Artech.ExtensionMethod.Extension::Adds( class  Artech.ExtensionMethod.Vector,
class  Artech.ExtensionMethod.Vector)

通过对IL的分析,我们基本上看出了Extension Method的本质。我们再来简单描述一下对Compiler的编译过程:当Compiler对Adds方法的调用进行编译的过程的时候,它必须判断这个Adds方式是Vector Type的成员还是以Extension Method的方式定义。Extension Method的优先级是最低的,只有确定Vector中没有定义相应的Adds方法的时候,Compiler才会在引用的Namespace中查看这些Namespace中是否定义有对应的Adds Extension Method的Static Class。找到后作进行相应的编译,否则出现编译错误。

五、一个完整的Extension Method的Sample

在介绍了Extension Method的本质之后,我们通过一个相对完整的Sample进一步了解Extension Method的运用,通过这个Sample,我们还可以粗略了解LINQ的原理。

C# 3.0为LINQ定义了一系列的Operator:select, from,where,orderby..., 促使我们按照OO的方式来处理各种各样的数据,比如XML,Relational DB Data,C#中IEnumeratable<T> Object。比如:

var names  =   new  List < string >   "Tom Cruise""Tom Hanks""Al Pacino""Harrison Ford" } ;
var result 
=  names.Where(name  =>  name.StartsWith( " Tom " ));
foreach (var name  in  result)
{
      Console.WriteLine(name);
}

我们通过上面的Code,从一系列的姓名列表中("Tom Cruise", "Tom Hanks", "Al Pacino", "Harrison Ford")筛选名字(First Name)为Tom的姓名。通过Where Operator,传入一个以Lambda Expression表示的筛选条件(name => name.StartsWith("Tom"))。Where Operator就是通过Extension Method的方式定义的。

在这里提供的Sample就是定义一个完成Where Operator相同功能的Operator,我们把这个Operator起名为When

using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Text;
using  System.Collections;

namespace  Artech.ExtensionMethod
{
    
public delegate TResult Function<Tparam, TResult>(Tparam param);

    
public static class Extension
    
{
        
public static IEnumerable<TSource> When<TSource>(this IEnumerable<TSource> source, Function<TSource, bool> predicate)
        
{
            
return new WhenEnumerator<TSource>(source, predicate);
        }
  

    }


    
public class WhenEnumerator<TSource> : IEnumerable<TSource>, IEnumerator<TSource>
    
{
        
private IEnumerable<TSource> _source;
        
private Function<TSource, bool> _predicate;
        
private IEnumerator<TSource> _sourceEnumerator;

        
public WhenEnumerator(IEnumerable<TSource> source, Function<TSource, bool> predicate)
        
{
            
this._source = source;
            
this._predicate = predicate;
            
this._sourceEnumerator = this._source.GetEnumerator();
        }


        
IEnumerable  Members

        
IEnumerable Members

        
IEnumerator  Members

        
IDisposable Members

        
IEnumerator Members
    }

}

我们来看看我们新的LINQ Operator:When的定义。我首先定义了一个Generic Delegate:Function。实际上他定义了一个一元函数y = f(x),TParam和TResult为参数和返回值得类型。 

public   delegate  TResult Function < Tparam, TResult > (Tparam param);

接着在Static Class Extesnion中定义了Extension Method:When。该方法包含两个参数,其中一个是执行筛选的数据源,另一个是用于判断数据源每个对象是否满足你所定义的筛选条件的断言。返回一个我们自定义的、实现了IEnumerable的WhenEnumerator对象。

public   static   class  Extension
    
{
        
public static IEnumerable<TSource> When<TSource>(this IEnumerable<TSource> source, Function<TSource, bool> predicate)
        
{
            
return new WhenEnumerator<TSource>(source, predicate);
        }
 
    }

WhenEnumerator的定义是实现When Extension Method的关键,我们现在着重来介绍它的具体实现。WhenEnumerator实现了Interface Enumerable<T>,为了简单,我们也它对应的Enumerator的实现也定义在同一个Class中,所以WhenEnumerator实现了两个Interface:IEnumerable<TSource>, IEnumerator<TSource>。 

以下3个成员分别代表:用于执行筛选的数据源、用于判断是否满足筛选条件的断言以及数据源的Enumerator对象。

private  IEnumerable < TSource >  _source;
private  Function < TSource,  bool >  _predicate;
private  IEnumerator < TSource >  _sourceEnumerator;

通过返回一个WhenEnumerator对象,实现了IEnumerable<TSource>的GetEnumerator()方法。 

         public  IEnumerator < TSource >  GetEnumerator()
        
{
            
return new WhenEnumerator<TSource>(this._source, this._predicate);
        }

对于另一个Interface IEnumerator<TSource>,直接调用数据源的Enumerator的同名方法实现了Current,和Reset()。对于MoveNext()则通过如下的方式实现:把当前的位置设置在下一个满足筛选条件的Element上

public   bool  MoveNext()
        
{
            
if (!this._sourceEnumerator.MoveNext())
            
{
                
return false;
            }


            
while (!this._predicate(this._sourceEnumerator.Current))
            
{
                
if (!this._sourceEnumerator.MoveNext())
                
{
                    
return false;
                }

            }


            
return true;
        }

到现在为止,这个新的LINQ Operator被创建,现在我们可以按照使用Where operator的方式来调用When。

我们可以通过Delegate的方式来使用When Operator:

class  Program
    
{
        
static void Main()
        
{
            var names 
= new List<string> "Tom Cruise""Tom Hanks""Al Pacino""Harrison Ford" };
            var result 
= names.When(delegate(string name) return name.StartsWith("Tom"); });
            
foreach (var name in result)
            
{
                Console.WriteLine(name);
            }

        }

}

输出结果:

Tom Cruise
Tom Hanks

我们也可以通过Lambda Expression的方式来使用When Operator:

static   void  Main()
        
{
            var names 
= new List<string> "Tom Cruise""Tom Hanks""Al Pacino""Harrison Ford" };
            var result 
= names.When(name=>name.StartsWith("Tom"));
            
foreach (var name in result)
            
{
                Console.WriteLine(name);
            }

        }

显然这种方式更简洁。 

Deferred Evaluation

对于LINQ,有一个非常重要的特征:Deferred Evaluation。在了解这个特征之前,我们来看一个例子:

static   void  Main()
{
            var names 
= new List<string> "Tom Cruise""Tom Hanks""Al Pacino""Harrison Ford" };
            var result1 
= names.When(name=>name.StartsWith("Tom"));
            names[
0= "Stephen Chou";
            var result2 
= names.When(name => name.StartsWith("Tom"));


            
foreach (var name in result1)
            
{
                Console.WriteLine(name);
            }


            
foreach (var name in result2)
            
{
                Console.WriteLine(name);
            }

}

运行程序,你会发现两个foreach loop显示的结果都是一样的:Tom Hanks。为什么result1实在第一个Element被改动之前返回的,但我们最终输出的结果却反映的是改动之后的数据源。通过我们上面的定义,你很容易得到答案。在这里我要说的是LINQ的一个重要的特性Deferred Evaluation:在调用Operator的时候并不会有任何的任何数据获取的过程,这个阶段的任务是创建一个同于获取数据的表达式。只要你真正所用到这个数据的时候,采用重数据源中通过你构建的表达式通过查询获取数据。

C# 3.x相关内容:
[原创]深入理解C# 3.x的新特性(1):Anonymous Type
[原创]深入理解C# 3.x的新特性(2):Extension Method - Part I
[原创]深入理解C# 3.x的新特性(2):Extension Method - Part II
[原创]深入理解C# 3.x的新特性(3):从Delegate、Anonymous Method到Lambda Expression
[原创]深入理解C# 3.x的新特性(4):Automatically Implemented Property
[原创]深入理解C# 3.x的新特性(5):Object Initializer 和 Collection Initializer

//============================================================================== // WARNING!! This file is overwritten by the Block UI Styler while generating // the automation code. Any modifications to this file will be lost after // generating the code again. // // Filename: D:\001 NX Model\000 NX Custom Development\ART_NX_C\ArkTech\Modeling_Custom.cs // // This file was generated by the NX Block UI Styler // Created by: jzk24 // Version: NX 2406 // Date: 08-30-2025 (Format: mm-dd-yyyy) // Time: 12:07 (Format: hh-mm) // //============================================================================== //============================================================================== // Purpose: This TEMPLATE file contains C# source to guide you in the // construction of your Block application dialog. The generation of your // dialog file (.dlx extension) is the first step towards dialog construction // within NX. You must now create a NX Open application that // utilizes this file (.dlx). // // The information in this file provides you with the following: // // 1. Help on how to load and display your Block UI Styler dialog in NX // using APIs provided in NXOpen.BlockStyler namespace // 2. The empty callback methods (stubs) associated with your dialog items // have also been placed in this file. These empty methods have been // created simply to start you along with your coding requirements. // The method name, argument list and possible return values have already // been provided for you. //============================================================================== //------------------------------------------------------------------------------ //These imports are needed for the following template code //------------------------------------------------------------------------------ using NXOpen; using NXOpen.BlockStyler; using NXOpen.UF; using NXOpen.Utilities; using System; using static NXOpen.BodyDes.OnestepUnformBuilder; using OnestepPart = NXOpen.BodyDes.OnestepUnformBuilder.Part; // 为另一个 Part 定义别名 using Part = NXOpen.Part; //------------------------------------------------------------------------------ //Represents Block Styler application class //------------------------------------------------------------------------------ public class Modeling_Custom { //class members private static Session theSession = null; private static UI theUI = null; private string theDlxFileName; private NXOpen.BlockStyler.BlockDialog theDialog; private NXOpen.BlockStyler.Group group1;// Block type: Group private NXOpen.BlockStyler.Enumeration enum0;// Block type: Enumeration private NXOpen.BlockStyler.StringBlock string0;// Block type: String private NXOpen.BlockStyler.StringBlock string01;// Block type: String private NXOpen.BlockStyler.Group group;// Block type: Group private NXOpen.BlockStyler.StringBlock string02;// Block type: String private Part workPart; //------------------------------------------------------------------------------ //Constructor for NX Styler class //------------------------------------------------------------------------------ public Modeling_Custom() { try { theSession = Session.GetSession(); theUI = UI.GetUI(); theDlxFileName = "Modeling_Custom.dlx"; theDialog = theUI.CreateDialog(theDlxFileName); theDialog.AddApplyHandler(new NXOpen.BlockStyler.BlockDialog.Apply(apply_cb)); theDialog.AddOkHandler(new NXOpen.BlockStyler.BlockDialog.Ok(ok_cb)); theDialog.AddUpdateHandler(new NXOpen.BlockStyler.BlockDialog.Update(update_cb)); theDialog.AddInitializeHandler(new NXOpen.BlockStyler.BlockDialog.Initialize(initialize_cb)); theDialog.AddDialogShownHandler(new NXOpen.BlockStyler.BlockDialog.DialogShown(dialogShown_cb)); } catch (Exception ex) { //---- Enter your exception handling code here ----- throw ex; } } //------------------------------- DIALOG LAUNCHING --------------------------------- // // Before invoking this application one needs to open any part/empty part in NX // because of the behavior of the blocks. // // Make sure the dlx file is in one of the following locations: // 1.) From where NX session is launched // 2.) $UGII_USER_DIR/application // 3.) For released applications, using UGII_CUSTOM_DIRECTORY_FILE is highly // recommended. This variable is set to a full directory path to a file // containing a list of root directories for all custom applications. // e.g., UGII_CUSTOM_DIRECTORY_FILE=$UGII_BASE_DIR\ugii\menus\custom_dirs.dat // // You can create the dialog using one of the following way: // // 1. Journal Replay // // 1) Replay this file through Tool->Journal->Play Menu. // // 2. USER EXIT // // 1) Create the Shared Library -- Refer "Block UI Styler programmer&#39;s guide" // 2) Invoke the Shared Library through File->Execute->NX Open menu. // //------------------------------------------------------------------------------ public static void Main() { Modeling_Custom theModeling_Custom = null; try { theModeling_Custom = new Modeling_Custom(); // The following method shows the dialog immediately theModeling_Custom.Launch(); } catch (Exception ex) { //---- Enter your exception handling code here ----- theUI.NXMessageBox.Show("Block Styler", NXMessageBox.DialogType.Error, ex.ToString()); } finally { if (theModeling_Custom != null) theModeling_Custom.Dispose(); theModeling_Custom = null; } } //------------------------------------------------------------------------------ // This method specifies how a shared image is unloaded from memory // within NX. This method gives you the capability to unload an // internal NX Open application or user exit from NX. Specify any // one of the three constants as a return Value to determine the type // of unload to perform: // // // Immediately : unload the library as soon as the automation program has completed // Explicitly : unload the library from the "Unload Shared Image" dialog // AtTermination : unload the library when the NX session terminates // // // NOTE: A program which associates NX Open applications with the menubar // MUST NOT use this option since it will UNLOAD your NX Open application image // from the menubar. //------------------------------------------------------------------------------ public static int GetUnloadOption(string arg) { //return System.Convert.ToInt32(Session.LibraryUnloadOption.Explicitly); return System.Convert.ToInt32(Session.LibraryUnloadOption.Immediately); // return System.Convert.ToInt32(Session.LibraryUnloadOption.AtTermination); } //------------------------------------------------------------------------------ // Following method cleanup any housekeeping chores that may be needed. // This method is automatically called by NX. //------------------------------------------------------------------------------ public static void UnloadLibrary(string arg) { try { //---- Enter your code here ----- } catch (Exception ex) { //---- Enter your exception handling code here ----- theUI.NXMessageBox.Show("Block Styler", NXMessageBox.DialogType.Error, ex.ToString()); } } //------------------------------------------------------------------------------ //This method launches the dialog to screen //------------------------------------------------------------------------------ public NXOpen.BlockStyler.BlockDialog.DialogResponse Launch() { NXOpen.BlockStyler.BlockDialog.DialogResponse dialogResponse = NXOpen.BlockStyler.BlockDialog.DialogResponse.Invalid; try { dialogResponse = theDialog.Launch(); } catch (Exception ex) { //---- Enter your exception handling code here ----- theUI.NXMessageBox.Show("Block Styler", NXMessageBox.DialogType.Error, ex.ToString()); } return dialogResponse; } //------------------------------------------------------------------------------ //Method Name: Dispose //------------------------------------------------------------------------------ public void Dispose() { if (theDialog != null) { theDialog.Dispose(); theDialog = null; } } //------------------------------------------------------------------------------ //---------------------Block UI Styler Callback Functions-------------------------- //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ //Callback Name: initialize_cb //------------------------------------------------------------------------------ public void initialize_cb() { try { group1 = (NXOpen.BlockStyler.Group)theDialog.TopBlock.FindBlock("group1"); enum0 = (NXOpen.BlockStyler.Enumeration)theDialog.TopBlock.FindBlock("enum0"); string0 = (NXOpen.BlockStyler.StringBlock)theDialog.TopBlock.FindBlock("string0"); string01 = (NXOpen.BlockStyler.StringBlock)theDialog.TopBlock.FindBlock("string01"); group = (NXOpen.BlockStyler.Group)theDialog.TopBlock.FindBlock("group"); string02 = (NXOpen.BlockStyler.StringBlock)theDialog.TopBlock.FindBlock("string02"); //------------------------------------------------------------------------------ //Registration of StringBlock specific callbacks //------------------------------------------------------------------------------ //string0.SetKeystrokeCallback(new NXOpen.BlockStyler.StringBlock.KeystrokeCallback(KeystrokeCallback)); //string01.SetKeystrokeCallback(new NXOpen.BlockStyler.StringBlock.KeystrokeCallback(KeystrokeCallback)); //string02.SetKeystrokeCallback(new NXOpen.BlockStyler.StringBlock.KeystrokeCallback(KeystrokeCallback)); //------------------------------------------------------------------------------ } catch (Exception ex) { //---- Enter your exception handling code here ----- theUI.NXMessageBox.Show("Block Styler", NXMessageBox.DialogType.Error, ex.ToString()); } } //------------------------------------------------------------------------------ //Callback Name: dialogShown_cb //This callback is executed just before the dialog launch. Thus any Value set //here will take precedence and dialog will be launched showing that Value. //------------------------------------------------------------------------------ public void dialogShown_cb() { try { //---- Enter your callback code here ----- } catch (Exception ex) { //---- Enter your exception handling code here ----- theUI.NXMessageBox.Show("Block Styler", NXMessageBox.DialogType.Error, ex.ToString()); } } //------------------------------------------------------------------------------ //Callback Name: apply_cb //------------------------------------------------------------------------------ public int apply_cb() { int errorCode = 0; try { workPart = theSession.Parts.Work; if (workPart == null) { theUI.NXMessageBox.Show("错误", NXMessageBox.DialogType.Error, "没有打开的零件文件"); return errorCode; } // 1. 将enum0的值赋予"型号"属性 string modelType = enum0.ValueAsString; SetPartProperty(workPart, "型号", modelType); // 2. 将string0的值赋予"DB_PART_NO"属性 string partNo = string0.Value; if (!string.IsNullOrEmpty(partNo)) { SetPartProperty(workPart, "DB_PART_NO", partNo); } else { theUI.NXMessageBox.Show("警告", NXMessageBox.DialogType.Warning, "零件编号不能为空"); } // 3. 将string01的值赋予"DB_PART_NAME"属性 string partName = string01.Value; SetPartProperty(workPart, "DB_PART_NAME", partName); // 4. 将string02的值设置为与string0相同 string02.Value = string0.Value; theUI.NXMessageBox.Show("成功", NXMessageBox.DialogType.Information, "属性已成功更新"); } catch (Exception ex) { //---- Enter your exception handling code here ----- errorCode = 1; theUI.NXMessageBox.Show("Block Styler", NXMessageBox.DialogType.Error, ex.ToString()); } return errorCode; } private void SetPartProperty(Part workPart, string propertyName, string propertyValue) { try { // 检查部件是否为空 if (workPart == null) { theUI.NXMessageBox.Show("错误", NXMessageBox.DialogType.Error, "部件为空"); return; } // 使用 UFUN 检查部件是否可写 if (!IsPartWriteable(workPart)) { theUI.NXMessageBox.Show("错误", NXMessageBox.DialogType.Error, "部件不可写"); return; } // 使用 UFUN 设置属性 UFSession ufSession = UFSession.GetUFSession(); int result= ufSession.Attr.SetStringUserAttribute( workPart.Tag, propertyName, 0, propertyValue, UFAttr.Equals); if (result !=0) { // 处理错误 theUI.NXMessageBox.Show("属性设置警告", NXMessageBox.DialogType.Warning, $"属性操作返回代码: {result}"); } } catch (Exception ex) { // 异常处理 theUI.NXMessageBox.Show("设置属性错误", NXMessageBox.DialogType.Error, $"无法设置属性 &#39;{propertyName}&#39;:{ex.Message}"); // 注意:这里不应该抛出 NotImplementedException } } // 添加检查部件可写状态的方法 private bool IsPartWriteable(Part part) { try { UFSession ufSession = UFSession.GetUFSession(); int writeStatus; object value = ufSession.Part.AskWriteStatus(part.Tag, out IsPartWriteable); // writeStatus 值为 0 表示部件可写 return IsPartWriteable; } catch { return false; } } //------------------------------------------------------------------------------ //Callback Name: update_cb //------------------------------------------------------------------------------ public int update_cb(NXOpen.BlockStyler.UIBlock block) { try { if (block == enum0) { string02.Value = string0.Value; } else if (block == string0) { //---------Enter your code here----------- } else if (block == string01) { //---------Enter your code here----------- } else if (block == string02) { //---------Enter your code here----------- } } catch (Exception ex) { //---- Enter your exception handling code here ----- theUI.NXMessageBox.Show("Block Styler", NXMessageBox.DialogType.Error, ex.ToString()); } return 0; } //------------------------------------------------------------------------------ //Callback Name: ok_cb //------------------------------------------------------------------------------ public int ok_cb() { int errorCode = 0; try { errorCode = apply_cb(); if (errorCode == 0) { theDialog.Dispose(); } } catch (Exception ex) { //---- Enter your exception handling code here ----- errorCode = 1; theUI.NXMessageBox.Show("Block Styler", NXMessageBox.DialogType.Error, ex.ToString()); } return errorCode; } //------------------------------------------------------------------------------ //StringBlock specific callbacks //------------------------------------------------------------------------------ //public int KeystrokeCallback(NXOpen.BlockStyler.StringBlock string_block, string uncommitted_value) //{ //} //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ //Function Name: GetBlockProperties //Returns the propertylist of the specified BlockID //------------------------------------------------------------------------------ public PropertyList GetBlockProperties(string blockID) { PropertyList plist = null; try { plist = theDialog.GetBlockProperties(blockID); } catch (Exception ex) { //---- Enter your exception handling code here ----- theUI.NXMessageBox.Show("Block Styler", NXMessageBox.DialogType.Error, ex.ToString()); } return plist; } } 发现以下错误:CS1503参数5:无法从"方法组"转换为"bool" CS1061"UFPart"未包含"AskWriteStatus"的定义,并且找不到可接受第一个"UFPart"类型参数的可访问扩展方法"AskWriteStatus"(是否缺少using指令或程序集引用?) CS1657"IsPartWriteable"是一个"方法组",无法用作 ref或cput值 CS0428无法将方法组"IsPartWriteable"转换为非委托类型"bcol"。是否希望调用此方法? CS0168 声明了变量"writeStatus",但从未使用过 CS0168 声明了变量"ex",但从未使用过 CS0168 声明了变量"ex",但从未使用过 CS0168 声明了变量"ex",但从未使用过,请更改
最新发布
09-02
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值