精华内容
下载资源
问答
  • IDL接口定义语言教程

    千次阅读 2013-07-02 11:36:57
    也叫“接口描述语言”(Interface Description Language),是一个描述软件组件接口的语言规范。 IDL用中立语言的方式进行描述,能使软件组件(不同语言编写的)间相互通信。 IDL通常用于RPC(Remote Procedure Call...


    IDL接口定义语言

     

    也叫“接口描述语言”(Interface Description Language),是一个描述软件组件接口的语言规范。 IDL用中立语言的方式进行描述,能使软件组件(不同语言编写的)间相互通信。 IDL通常用于RPC(Remote Procedure Call,远程过程调用)软件。 IDL提供了一个“桥”来连接不同的系统。

     

    基于IDL的软件系统包括:

    Sun公司的ONCRPC,

    The Open Group的DistributedComputing Environment(分布式计算环境),

    IBM的SystemObject Model(系统对象模型),

    OMG组织的CORBA,

    Facebook公司的Thrift,

    用于WebService的WSDL。

     

    IDL Specification Language

    类似于其它的接口描述语言,IDL以独立于语言和硬件的方式来定义接口,允许组件间的接口规范采用不同语言编写,通过RPC可以执行在不同的机器上。

    Java IDL把CORBA的能力移植到Java平台,提供了基于标准的互操作能力和连通性。Java IDL使分布式带Web功能的Java应用程序能够透明地调用使用业界标准的OMG IDL的远程网络服务。

    Java IDL技术是基于接口的,定义在CORBA IDL之上,为想使用Java编程语言的CORBA程序员进行编程的。这就是“Business as usual”(照常营业,一切正常)的CORBA编程,支持Java技术,同样也支持C++或COBOL。

    IDL(Interface Definition Language)即接口定义语言,是CORBA规范的一部分,是跨平台开发的基础。IDL提供一套通用的数据类型,并以这些数据类型来定义更为复杂的数据类型。可变化 IDL 基本类型 整数类型 OMG IDL 摒弃int 类型在不同平台上取值范围不同带来的多义性的问题。常数定义常数可以是整数、字符、浮点数、字符串、Boolean、octet 或枚举型,不能是 any 类型或用户定义的类型。OMG IDL数组类型IDL array 和sequence,可以轻易地被映射到实现语言中。序列可以包含所有类型的元素,不管是基本类型还是用户定义的类型。


    OMG IDL文件概述

      从本质上讲,OMG IDL接口定义语言不是作为程序设计语言体现在CORBA体系结构中的,而是用来描述产生对象调用请求的客户对象和服务对象之间的接口的语言。OMG IDL文件描述数据类型和方法框架,而服务对象则为一个指定的对象实现提供上述数据和方法。
      OMG IDL文件描述了服务器提供的服务功能,客户机可以根据该接口文件描述的方法向服务器提出业务请求。在大多数CORBA产品中都提供IDL到相关编程语言的编译器。程序设计人员只需将定义的接口文件输入编译器,设定编译选项后,就可以得到与程序设计语言相关的接口框架文件和辅助文件。

    IDL文件应用过程如图1所示。

     

     

    图1OMG IDL文件编译过程 

    在语法规则方面,类似于C++或Java中关于接口或对象的定义,OMG IDL增加了一些构造方法支持IDL特有的方法调用机制。OMG IDL只是一种说明性的语言,支持C++语法中的常量、类型和方法的声明。采用OMG IDL这样的说明性语言,其目的在于克服特定编程语言在软件系统集成及互操作方面的限制,这正是CORBA的诱人之处,同样也体现出采用CORBA构造分布式应用程序在网络时代的强大生命力。OMG IDL已经为C、C++、Java等主要高级程序设计语言制定了IDL到高级编程语言的映射标准。项目开发人员可以根据需要选择自己最熟悉的编程语言来独立开发基于CORBA的应用,而对软件系统的互操作能力不产生影响。
    OMG IDL的语法规则

    1. OMG IDL文件举例

      module Compute
      { typedef double radius;
      typedef long times;
      interface PI
      { double getResult( in radiusaRadius, in times time); }
      }
      上述接口定义文件主要用于客户端向服务对象提出请求:计算π值。因此,模块中定义了一个方法getResult(),以圆的直径(aRadius)和迭代次数(times)作为该方法的输入参数。

    2. OMG IDL词法规则

      OMG IDL采用ASCII字符集构成接口定义的所有标识符。标识符由字母、数字和下划线的任意组合构成,但第一个字符必须是ASCII字母。IDL认为大写字母和小写字母具有相同的含义,例如anExample和AnExample是相同的。

    与C++和Java类似,采用以“/*”开始,以“*/”结束来注释一段代码,以“//”开始注释从“//”开始直至行尾的所有内容。
      另外,IDL保留了47个关键字,程序设计人员不能将关键字用作变量或方法名。需要注意的是关键字的大小写,例如:
      typedef double context;
      //错误:定义的变量context是关键字
      typedef double CONTEXT;
      //错误:CONTEXT与关键字context冲突

    3. 数据类型

      (1)基本数据类型:OMG IDL基本数据类型包括short、long和相应的无符号(unsigned)类型,表示的字长分别为16、32位。
      (2)浮点数类型:OMG IDL浮点数类型包括float、double和longdouble类型。其中float表示单精度浮点数,double表示双精度浮点数,long double表示扩展的双精度浮点数。
      (3)字符和超大字符类型:OMG IDL定义字符类型char为面向字节的码集中编码的单字节字符; 定义类型wchar为从任意字符集中编码的超大字符。
      (4)逻辑类型:用boolean关键字定义的一个变量,取值只有true和false。
      (5)八进制类型:用octet关键字定义,在网络传输过程中不进行高低位转换的位元序列。

    (6)any数据类型:引入该类型用于表示OMG IDL中任意数据类型。

    4. 常量

      OMG IDL用const关键字声明一个常量,用于模块(module)或接口(interface)中定义保持不变的量,如:
      const double PI = 3.1415926;
      在IDL中,可以定义long、unsigned long、unsigned short、char、boolean、float、double、string类型的常量。

    5. 构造数据类型

      类似于C和C++的语法规则,OMG IDL中构造数据类型包括结构、联合、枚举等形式。如下例:
    (1)结构类型:
      typedef long GoodsNumber;
      struct
      { GoodsNumber number;
      string name;
      float price; }
    (2)联合类型:
      union stockIn switch( short )
      { case 1: stocker : long;
      case 2: goodsName1 : string;
      case 3: goodsName2 : string; }
    (3)枚举类型:
      enum GoodsStatus { GOODS_SALED,GOODS_INSTOCK};

    6. 数组类型

      OMG IDL的数组类型提供了多维定长、统一数据格式的数据存储方式——数组。每一维的长度必须在定义时给定,所有数据单元必须存储相同类型的元素。如下例定义一个长度为20×100的整数数组:
      typedef long aDimension[20][100];

    7.模板(template)类型

      OMG IDL提供两种类型的模板:
    (1) 序列(sequence)类型:
      用该方法定义长度可变的任意数值类型的存储序列,通常在定义时可以指定长度,也可以不指定,如:
      typedef sequence <long,80> aSequence;
      //长度定义为80
      typedef sequence <long>anotherSequence;
      //长度不定
    (2) 字符串(string)序列:
      同样对于字符串序列类型,也有两种定义方式:
      typedef string <80> aName;//长度定义为80
      typedef string anotherName; //长度不定

    8.接口(interface)

      在前几讲中,均提到了CORBA接口,接口作为服务对象功能的详细描述,封装了服务对象提供服务方法的全部信息,客户对象利用该接口获取服务对象的属性、访问服务对象中的方法。
      接口用关键字interface声明,其中包含的属性和方法对所有提出服务请求的客户对象是公开的,如下例:
      interface JobManager
      { readonly attribute stringFirstName;
      attribute string status;
      string QueryJobStatus( in longNumber, out string property); }

     

    CORBA

    CORBA(CommonObject Request Broker Architecture公共对象请求代理体系结构)是由OMG组织制订的一种标准的面向对象应用程序体系规范。或者说CORBA体系结构是对象管理组织(OMG)为解决分布式处理环境(DCE)中,硬件和软件系统的互连而提出的一种解决方案;OMG组织是一个国际性的非盈利组织,其职责是为应用开发提供一个公共框架,制订工业指南和对象管理规范,加快对象技术的发展。
      OMG组织成立后不久就制订了OMA(ObjectManagement Architecture,对象管理体系结构)参考模型,该模型描述了OMG规范所遵循的概念化的基础结构。OMA由对象请求代理ORB、对象服务、公共设施、域接口和应用接口这几个部分组成,其核心部分是对象请求代理ORB(ObjectRequest Broker)。对象服务是为使用和实现对象而提供的基本服务集合;公共设施是向终端用户应用程序提供的一组共享服务接口;域接口是为应用领域服务而提供的接口;应用接口是由开发商提供的产品,用于它们的接口,不属于OMG标准的内容。ORB提供了一种机制,通过这种机制,对象可以透明的发出请求和接收响应。分布的、可以互操作的对象可以利用ORB构造可以互操作的应用。
      CORBA标准由对象管理组织(OMG)设立并进行控制,CORBA定议了一系列API,通信协议,和物件/服务信息模型用于使得异质应用程序能够互相操作,这些应用程序用不同的程序语言编写,运行在不同的平台上。CORBA因此为定义明确的物件提供了平台和位置的透明性,这些物件是分布式计算平台的基础。
      CORBA分布计算技术,是由绝大多数分布计算平台厂商所支持和遵循的系统规范技术,具有模型完整、先进,独立于系统平台和开发语言,被支持程度广泛的特点,已逐渐成为分布计算技术的标准。COBRA标准主要分为3个层次:对象请求代理、公共对象服务和公共设施。最底层是对象请求代理ORB,规定了分布对象的定义(接口)和语言映射,实现对象间的通讯和互操作,是分布对象系统中的"软总线";在ORB之上定义了很多公共服务,可以提供诸如并发服务、名字服务、事务(交易)服务、安全服务等各种各样的服务;最上层的公共设施则定义了组件框架,提供可直接为业务对象使用的服务,规定业务对象有效协作所需的协定规则。
      CORBA(公共对象请求代理架构):这是个和微软com,com+齐名的同类软件技术规范,由OMT提出。
      用于在不同进程(程序)之间,甚至是不同物理机器上的进程(程序)之间通讯。底层技术依靠RPC[远程过程调用]实现。
      面向对象的软件,以数据为中心设计,对象类既具有模块的封装性和类属等特性,还具有继承特性
      ,极大地提高了类的可扩充性和可再用能力。对象类较之于传统软件的功能模块而另具有的优点是:
      (1)易于理解,具有完整的语义特征;
      (2)易于扩充和修改,具有较高的通用性和适应性;
      (3)易于构造组装,具有规范的外部接口。
      开发应用组件必须遵循标准,以保证软件组件的互操作性,只有遵循统一的标准,不同厂商的、不同时期的、不同程序设计风格的、不同编程语言的、不同操作系统的、不同平台上的软件或软件部件才能进行交流与合作。为此,OMG(ObjectManageGroup)提供了一个对象标准CORBA,它定义了一个网连对象的接口,使得对象可以同时工作。基于CORBA的对象请求代理ORB为客户机/服务器开发提供了中间件的新格式。
      作为OMG成员的微软公司撇开CORBA而另辟了COM(ComponetObjectModel),即组件对象模型,并把COM定位成基于对象的软件开发模型,尽管COM被认为是微软鼓噪出来的技术,但支持COM的开发工具却不断增多,其中大部分来自于微软,包括VisualBasic和VisualC++。
      公共对象请求代理结构:CORBA标准
      全球性网络使线上的所有设备和软件成为全球共享的浩瀚的资源,计算机环境也从集中式发展到分布式环境,开放式系统的发展使用户能够透明地应用由不同厂商制造的不同机型不同平台所组成的异构型计算资源,因此,分布式处理和应用集成自然而然地成为人们的共同要求,那么什么是分布式处理和应用集成呢?它们的功能和关键技术是什么呢?简单地讲,分布式处理和应用集成就是指在异构的、网络的、物理性能差别很大的、不同厂商的、不同语言的信息资源的基础上构建信息共享的分布式系统,并且能够有效地进行应用系统和分布式处理的集成。分布式处理的关键在于定义可管理的软件构件,即面向对象技术中的“对象”。应用集成的关键在于为跨平台、跨机种、跨编程语言的产品提供统一的应用接口。OMG组织针对当今信息产业的要求,公布了CORBA标准,即公共对象请求代理体系结构(CommonObject Request Broker Architecture),这是一个具有互操作性和可移植性的分布式面向对象的应用标准。
      CORBA的核心是对象请求代理ORB,它提供对象定位、对象激活和对象通讯的透明机制。客户发出要求服务的请求,而对象则提供服务,ORB把请求发送给对象、把输出值返回给客户。ORB的服务对客户而言是透明的,客户不知道对象驻留在网络中何处、对象是如何通讯、如何实现以及如何执行的,只要他持有对某对象的对象引用,就可以向该对象发出服务请求。
      CORBA允许用户以两种不同的方式提出对象请求:
      1)静态调用:
      通过给定接口的存根,在编译了对象代码后,进入客户端的程序。因此,静态调用必须在编译时就知道对象及其类型。
      2)动态调用:
      通过ORB的动态调用接口DII,在运行时生成访问对象的代码。
      不管客户以哪一种形式提出请求,ORB的任务是:找出所要对象的位置,激活该对象,向对象传递此请求。对象执行所请求的服务后,把输出值返回给ORB,然后再由ORB返回给客户。
      CORBA的重要概念是:

      1.对象连接

      CORBA广泛地支持对象的实现,在单服务器系统中也可以实现由接口定义语言定义的接口。ORB的灵活性既可以直接集成已有的应用,又不会使新对象受某些原则的制约。
      对象连接提供了有不同类型对象实现时,使用ORB服务的方法,服务包括:对象引用、方法调用、安全控制、对象实现的激活与静候等。

      2.接口定义语言(IDL)

      CORBA用IDL来描述对象接口,IDL是一种说明性语言,它的语法类似于C++。
      IDL提供的数据类型有:基本数据类型、构造类型、模板类型、和复合类型、操作说明。这些类型可以用来定义变元的类型和返回类型,操作说明则可以用来定义对象提供的服务。
      IDL还提供模块构造,其中可以包含接口,而接口是IDL各类型中最重要的,它除了描述CORBA对象以外,还可以用作对象引用类型。
      IDL提供了接口继承性,派生接口可以继承其基类接口所定义的操作与类型。IDL的接口继承性有其特殊性,此处不赘述。
      总之,CORBA的IDL是一种说明性语言,描述面向对象系统开发所遵循的接口与实现相分离的基本原则。

      3.动态调用接口

      把IDL说明编译成面向对象程序设计语言的实代码后,客户可以调用已知对象的操作。在某些应用中,用户并不了解应用接口编译信息,但也要求调用对象的操作,这时就要动态调用接口来调用用户的操作了。例如,图形用户接口应支持用户浏览接口公共库,以获得每个对象所支持的操作信息,用户可根据自己的需求从浏览对象中挑选出所需的对象操作,具体的对象操作的调用实际上是用动态调用接口来完成的。

      4.接口公用库

      接口公用库持久地存储IDL的接口说明,借助于接口公用库,可以实现对象继承性层次结构的导航,并且提供了有关对象支持的所有操作的描述。接口公用库最常见的功能是为接口浏览器提供信息,帮助应用开发者找出潜在的可重用的软件部件。ORB可以利用接口公用库检查运行时的操作参数类型,但接口公用库的基本功能是提供类型信息,为动态调用接口发送请求提供信息支持。
      CORBA的不足之处:
      尽管有多家供应商提供CORBA产品,但是仍找不到能够单独为异种网络中的所有环境提供实现的供应商。不同的CORBA实现之间会出现缺乏互操作性的现象,从而造成一些问题;而且,由于供应商常常会自行定义扩展,而CORBA又缺乏针对多线程环境的规范,对于像C或C++这样的语言,源码兼容性并未完全实现。
      CORBA过于复杂,要熟悉CORBA,并进行相应的设计和编程,需要许多个月来掌握,而要达到专家水平,则需要好几年。

     

    一、什么是Java IDL

    ----Java IDL(InterfaceDefinition Language)可实现网络上不同平台上的对象相互之间的交互,该技术基于通用对象请求代理体系结构CORBA规范说明。IDL是不依赖于语言的接口定义语言,所有支持CORBA的语言都有IDL到该语言的映射。就像其名字所表示的那样,Java IDL支持到Java语言的映射。CORBA规范说明和IDL映射是由OMG(ObjectManagement Group)定义的。OMG由700多个成员组成,Sun公司是其成员之一,它在定义IDL到Java映射的工作中起了主要作用。

    ----JDK1.1给Java开发人员提供了开发100%纯Java分布式应用的功能,即远程方法调用Java RMI。而Java2平台提供的JavaIDL可以在分布式应用中使用非Java语言,也就是说,Java2平台提供的ORB(ObjectRequest Broker)可以和任何遵从CORBA规范的ORB互操作,包括IONAT echnologies的Orbix、Visigenic Software的Visi Broker、IBM的Component Broker等。目前,主要的Web浏览器(MicrosoftIE4.0和NetscapeNavigator4.0)实现的主要是JDK1.1中的功能。不过,利用Sun公司提供的Java插件(Plug-in)可以使浏览器具备Java2平台的所有特征。

    ----需要说明的是,Java2平台提供了两种不同的方法来构造分布式应用系统,即Java RMI和Java IDL,它们具有相似的特征和功能,Java RMI支持用Java语言写的分布式对象,Java IDL可以与支持CORBA的任何程序设计语言如C、C++、COBOL等写的分布式对象交互。这两种方法各自具有不同的特点:

    ----(1)100%纯Java和对遗产应用系统(legacy system)的支持。JavaRMI是对分布式应用系统的100%纯Java解决方法,具有Java的"Writeonce, run anywhere"的优点。用JavaRMI开发的应用系统可以部署在任何支持Java运行环境的平台上。

    ----相反,JavaIDL是基于CORBA规范标准的技术,可以远程调用非Java语言编写的对象,因此Java IDL提供了对那些用非Java语言开发的遗产应用系统的支持。

    ----(2)使用不同的通信协议。Java RMI和Java IDL目前使用不同的通信协议,Java IDL使用CORBA/IIOP协议,IIOP(InternetInter-ORBProtocol)协议可以使位于不同平台上、用不同语言写的对象以标准的方式进行通信;Java RMI目前使用Java远程消息交换协议JRMP(Java Remote Messaging Protocol)进行通信,JRMP是专为Java的远程对象制定的协议,不过Sun和IBM已经宣布将来会支持在RMI中使用IIOP协议,以便和遵从CORBA规范的远程对象通信。

    ----(3)通过引用调用对象还是通过值调用对象。在Java IDL中,客户端通过引用与远程对象交互,即客户机使用桩(Stub)对远程服务器上的对象进行操作,但并不拷贝服务器上的对象。

    ----相反,RMI使得客户机可以通过引用和远程对象交互,也可以把远程对象下载到客户机运行环境进行操作,由于在RMI中使用的对象都是Java对象,因此RMI使用Java中的对象串行化(Serialization)功能在服务器和客户机之间传输对象。不过CORBA规范的以后版本将包括按值调用对象的功能。

    ----Java RMI和JavaIDL各有自己的优缺点,从某种意义上说,RMI可以看作是RPC(Remote Procedure Calls)的面向对象版本。RMI的最大优势是可以用它来提供100%纯Java的解决方案,这意味着构造RMI应用系统将比较简单,但这也正是基于RMI的应用系统的一个缺点,即只能在Java环境中运行,不能充分利用遗产应用系统。

    ----Java RMI和JavaIDL均可满足一定范围的用户需求,都适用于一定范围的应用,两者之间存在着重叠,有些应用可使用两者中的任何一种技术开发,但对于某些应用来说,采用其中的某一种比采用另一种更为恰当。

    二、使用CORBA和JavaIDL

    ----从1989年以来,OMG一直致力于开放的软件总线体系结构CORBA的规范说明的定义,利用CORBA,不同供应商开发的、运行在不同平台上的构件可以互操作,而不管该对象位于何处,用什么语言实现。Java IDL使得Java也支持CORBA规范说明。作为Java企业计算API的一部分,Java IDL可以保证企业异质计算中的无缝互操作性和可连接性。

    ----CORBA对象和一般的程序设计语言中的对象的区别在于:

      CORBA对象可以位于网络中的任何位置;

      CORBA对象可以和其他平台上的对象交互;

      CORBA对象可以用任何程序设计语言编写,只要有IDL到该语言的映射即可(目前已包括到Java、C++、C、Smalltalk、COBOL、Ada等语言的映射)。

    ----接口定义语言IDL用于定义CORBA对象的接口,所有的CORBA对象都支持IDL接口。IDL语法和C++非常类似,利用Java IDL,可以在Java中定义、实现、存取CORBA对象。对于每个IDL,idl to java生成一个Java接口和其他一些必要的.java文件,包括一个客户端桩(Stub)和服务器端骨架(Skeleton)。

    ----为了使用JavaIDL,需要有idl to java编译器,idl to java产生和任何遵从CORBA规范的ORB一起工作的客户端桩和服务器端骨架,Java2平台中包括CORBAAPI和ORB,使得Java应用系统通过IIOP协议可以调用远程的CORBA对象,JavaORB支持暂态(Transient)CORBA对象,提供了暂态名字服务器把对象组织成树目录结构,名字服务器遵从CORBA中COS规范说明的命名服务规范说明(Naming Service Specification)。

    ----图1说明了从客户端到服务器发送一个消息请求的过程,其中客户端也可以是一个CORBA对象:

    ----客户机不需要了解CORBA对象的位置与实现细节,也不需要了解哪个ORB用于存取对象。

    ----在客户端,应用系统包括远程对象的引用,对象引用使用桩方法作为远程方法的代理,这个方法事实上在ORB中的,所以调用桩方法会调用ORB的连接功能,ORB会把对桩方法的调用传递到服务器端。

    ----在服务器端,ORB利用骨架代码把远程调用转换成本地对象的方法调用,骨架需要对调用和参数的格式进行转换。同时,当方法返回时,骨架对结果进行变换,然后通过ORB把结果返回给客户机。

    ----不同的ORB之间通过IIOP协议进行通信,IIOP是建立在TCP/IP之上的协议。

    ----CORBA目前还处于发展阶段,一些标准还在定义之中,但CORBA中的大部分基本结构已经定义,许多软件供应商已根据CORBA的定义作了很多开发工作,Java IDL就是该规范说明的一个实现。但在Java2平台中,并没有实现CORBA规范说明中的所有特征,如:

      接口库:在一般的Java IDL操作中并不需要接口库,Java客户机可以存取其他的接口库,如C++ORB提供的接口库;

     RMI/IIOP协议;

      按值调用对象;

      IDL中的一些类型如wchar、wstring、longdouble等。

    三、IDL到Java的映射

    ----为了使Java支持CORBA规范说明,需要一个把IDL中的元素映射为Java中元素的标准途径,Sun已经制订了两者之间的映射规则,并提供了编译器idl to java,以便从IDL得到相应的桩和骨架。

    ----下表列出了一些IDL中元素和Java中元素的映射关系。

    ----和Java中的接口一样,IDL接口不包含方法的具体实现,Java开发人员需在Java类中提供对这些方法的具体实现。

    四、使用Java IDL开发应用的过程及实例

    ----用JavaIDL开发分布式应用系统一般可分为五个步骤:

    ----1.定义远程接口

    ----用IDL定义远程对象的接口,使用IDL而不是Java语言是因为idl to java编译器可以自动地从IDL产生Java语言的桩和骨架源文件,以及和ORB连接时所需要的一些代码。使用IDL,开发人员可以用其他语言来实现客户机和服务器。如果要为一个已经存在的CORBA服务实现客户机,或为一个已经存在的客户机实现服务,则首先要给出IDL接口,然后运行idl to java编译器产生桩和骨架,在此基础上再进行实现。

    ----2.编译远程接口

    ----在IDL文件运行idl to java编译器,产生Java版本的接口,以及桩和骨架代码文件,这些代码文件使得应用程序可以和ORB相连接。

    ----3.实现服务器

    ----把idlto java编译器产生的骨架和服务器应用程序集成在一起,除了要实现远程接口中的方法之外,服务器代码还要包括启动ORB以及等待远程客户机的调用等部分。

    ----4.实现客户机

    ----类似地,以桩作为客户端应用程序的基础,客户机建立在桩之上,通过Java IDL提供的名字服务查询服务器,获得远程对象的引用,然后调用远程对象中的方法。

    ----5.启动应用程序

    ----一旦实现了服务器和客户机,就可以启动名字服务,接着启动服务器,然后运行客户机。

    ----下面以一个经典的HelloWorld程序来说明具体的开发过程。HelloWorld包含一个操作,该操作返回一个字符串并打印出来,Client和Server之间的通信过程如下:

    ----(1)客户端应用程序或小应用程序调用HelloServer的sayHello操作;

    ----(2)ORB把调用传递到已注册的服务对象;

    ----(3)服务对象运行sayHello方法,返回一个Java字符串;

    ----(4)ORB把该字符串返回给客户机;

    ----(5)客户机打印该字符串的值。

    ----尽管HelloWorld程序比较简单,但它涉及到的任务和几乎任何一个使用CORBA静态调用的分布式应用系统所涉及到任务相同,图2说明了如何用CORBA在客户机和服务器之间实现经典的"HelloWorld"程序。

    ----首先是定义Hello.idl,下面是用IDL描述的Hello.idl,只有一个操作(方法),该操作返回一个字符串。

     Module Hello App

      {

     interface Hello

      {

     stringsay Hello ( );

      };

      };

    ----然后把Hello.idl文件映射为Java源代码文件,用以下命令编译Hello.idl文件:

    ----idl to javaHello.idl

    ----根据命令行中的不同选项,idl to java编译器产生不同的文件,上面这条命令将创建子目录HelloApp并在其中产生五个文件:_HelloImplBase.java、_HelloStub.java、Hello.java、HelloHelper.java、HelloHolder.java。

    ----要完成该应用程序,还要分别在这五个文件的基础上提供服务器端和客户机端的实现。

    ----CORBA服务器程序的结构和大部分Java应用程序的结构一样,即导入所需要的包,声明服务器类,定义main方法,处理一些例外等。另外,CORBA服务器要有一个ORB对象,每个服务器实例化一个ORB,并向其注册服务对象,因此当ORB接收到调用请求时可以寻找到服务器。最后是服务对象的管理,服务器是一个进程,实例化了一个或多个服务对象,服务对象具体实现接口中说明的操作。HelloServer和命名服务一起工作,使得服务对象对于客户机来说是可用的,服务器需要对名字服务的对象引用,可以向名字服务注册,并保证向Hello接口的调用被路由到其服务对象上,最后是等待客户机的调用。

    ----CORBA客户机的结构和大部分Java应用程序的结构基本相似,即导入所需要的包、声明应用类、定义main方法、处理一些例外等。另外和服务器程序一样,一个CORBA客户机也需要本地的ORB来执行所有的配置工作,每个客户机实例化一个org.omg.CORBA.ORB对象,然后向该对象传递一些必要的信息以进行初始化,最后是利用ORB取得所需要的服务。一旦一个应用有了ORB,即可通过ORB来确定应用所需要的服务的位置,在本例子中即是Helloserver。为了调用CORBA对象中的操作,客户端应用要有对该对象的引用,有很多种方法可以得到这种引用,如调用ORB.resolve_initial_references或使用其他的CORBA对象(如命名服务),当在分布式环境中没有命名服务可用时,CORBA客户机使用字符串化对象引用(Stringifiedobjectreference)来得到其第一个对象。

     

     

    1.4 接口定义语言(IDL)

    一个IDL文件定义公共的应用程序接口(API),通过服务程序中的对象向外公布。一个CORBA对象的类型称作接口,与C++中的类或者Java中的接口类似。IDL接口支持多重继承。

    一个IDL文件示例如图1.1所示。IDL接口可以包括方法和属性。很多人都误以为一个IDL接口的属性类似于C++中的示

    例变量(或者Jave中的域)的概念。这是错误的。一个属性只是在语法上对一对儿get-和set-类型方法的称谓。属性可以是只读的,这种情况时该属性只有一个get-类型的方法。

    module Finance {

       typedef sequence<string> StringSeq;

       struct AccountDetails {

       string name;

       StringSeq address;

       long account number;

       double current balance;

        };

       exception insufficientFunds { };

       interface Account {

           void deposit(in double amount);

           void withdraw(in double amount)

               raises(insufficientFunds);

           readonly attribute AccountDetails details;

        };

    };

    *****************************************************************************************************

    图1.1: IDL文件示例

    方法的参数有一个指定的方向,可以是in(意味着参数由客户程序传入服务程序),out(参数从服务程序传回客户程序)或者inout(参数是双向传递的)。方法同样可以有返回值。方法在某块出错时可以引发(抛出)一个异常。有30多种预定义的异常类型,称作系统异常,他们都可以由方法抛出,尽管在实际系统中CORBA运行时系统引发的异常多于应用程序代码引发的。除了预定义的系统异常,新的异常类型可以在IDL文件中定义,这些异常可称为用户定义的异常。方法署名的raises子句指定该方法可能抛出的用户定义异常。

    方法的参数(包括返回值)可以是内置类型的一种——例如:string,boolean或者long,也可以是IDL文件中定义的用户自定义的类型。用户定义类型可以是一下的任意一种:

    结构体。类似于C/C++中的结构体或者Java中只包含公共域的类。

    序列。集合类型,就像一个可以增或减的一维数组。

    数组。IDL数组的维度在IDL文件中指定,所以数组是固定大小的,这就是说他不能在运行时增或者减。数组在IDL中很少用到。序列类型更灵活,所以用的更多。

    自定义。给现存的类型定义一个新名字。例如,下面的语句定义age可用来描述short。

       typedef short age;

    默认情况下,IDL的序列和数组是匿名类型,也就是说他们没有名字。通常使用typedef的重要意义在于给序列或者数组的声明关联一个名字。例句可以在图1.1中StringSeq的定义中看到。

    联合体。该类型可以在运行时保留多个值中的一个。例如:

    union Foo switch(short) {

       case 1: boolean boolVal;

       case 2: long longVal;

       case 3: string stringVal;

    };

    一个Foo类型的实例可以保存一个boolean、long或者string值。前面的case标记(成为判别式)表明当前是哪种值。与IDL联合体相同的理念可以在很多面向过程的语言中找到。但是,他们很少在面向对象的语言中使用,因为多态统称能以更好的方式达到同样的目的。

    枚举类型。枚举在概念上类似于一个常整型集合的声明。例如:

    enum color (red, green, blue );

    enum city (Dublin, London, Paris, Rome );

    从本质上说,CORBA使用整数表达不同的枚举值,使用枚举声明的好处在于很多编程语言都有对它的内在支持,或者相似的机制,可以用来进行强类型判识,这样程序员就不会将city变量和color变量相加。

    定点类型。定点类型保存定点数值,而float和double类型保存浮点数值。浮点计算适用于很多用途,但是,可能导致几个小数位后的舍入误差。对比来说,定点数值可能比对应的浮点数值占用更多的内存空间,但是他们有避免舍入误差的优点。使用定点数值趋向于限制在适当的应用领域。例如金融计算和数字信号处理等。尽管一个工程使用定点类型数字,它很可能使用定点计算作为细节实现却不将定点数字的使用公布为公共的IDL接口。由于以上原因,定点类型很少在IDL文件中声明。

    值类型。在9.2节、96页讨论。

    IDL类型可以组织在一个module中。module结构与C++中的namespace或者Java中的package有异曲同工之妙,也就是说,他事先给类型名称前添加了一个前缀,以避免命名空间冲突。IDL中的域操作符是“::”。例如,

    Finance::Account是Finace模块中定义的Account类型的全域名

     

    编译 IDL 后生成的 Java 类

    Helper         

    为接口客户提供有用的助手功能的类。编译器为 narrow 功能自动生成代码,这种 narrow 功能让客户将CORBA对象引用强制转换为接口类型。还提供了绑定(bind)功能,用户可以用其查找该类型的对象。

     

    Holder         

    含有接口类型的公共实例成员的类。用户和服务器用其来以方法调用的 out 和 inout 参数的形式传递接口类型的对象

     

    Stub           

    为接口对象实现客户端存根的类,它是真正的提供排列功能的接口的内部实现。

     

    Operations

    定义了 IDL 功能的类。

     

    Tie            

    CORBA支持两种类型的程序:基于继承(inheritance-based)的和基于委托的(delegantion-based)。Tie类只是扩展了POA,但它没有提供自己的实现语义。它把所有的工作委托到一个实现对象上。每一个Tie对象存储一个实现对象的引用。

    委托方法可用两种类实现IDL接口:1.一个IDL生成的Tie类,该类继承自POA,但是委托所有的调用到一个实现类;2.一个实现IDL生成的Operations接口的类,它定义了IDL的功能。

     

    POA            

    为接口实现CORBA服务器端框架的类。这个类将 CORBA 和 Java 对象模型组合到一起。它是通过使用一个实现了Java的org.omg.CORBA.Object接口的 Java 对象做到这一点的。这是CORBA根接口,所有得CORBA对象都必须实现它。

     

     

    CORBA IDL-to-Java 映射

    通用结构:

    1.CORBA模块

     ORBA IDL 模块(module)映射成与IDL模块同名的Java包

    2.CORBA异常

     CORBA定义了两类异常

     I.system exceptions               一种被CORBA定义的异常

     II.user-defined exceptions      由用户在IDl中定义的异常         

    3.CORBA 参数

     CORBA定义了三种参数传递模式:in、out和 inout。Java 只支持in 。         

    4.CORBAHolder 类

     由于Java中没有IDLout 和 inout 的对应物,对IDL out 和inout 参数,IDL-to-Java 映射必须提供一些附加的机制来支持值传递(并返回结果)。

     映射定义 Holder 类,这些 Holder 类在Java中实现附加的参数传递模式。对每个IDL out 或 inout 参数,客户必须实例化一个适当的 Holder 类的实例(它是通过值传递的)。

    5.CORBAHelper 类

     Helper 类包含用于不同方式操作IDL的方法。

     Helper 类提供客户可以用来操作类型的静态方法,这些包括该类型的任何插入和取出操作、获得库(repository)ID、获得类型码(typecode)、从流中读取类型并把类型写入流中。

     另外,映射IDL接口的Helper类提供一个静态的narrow方法,可以用于进行强制类型转换。

    6.CORBA属性

     CORBA IDL 接口可以拥有属性,这些都是类型域中set和get操作所需的。

     每个属性都被映射到一对与属性同名的重载Java访问器和修改器方法上。

    结构类型:

    1.sequence(序列)

    一个可变大小的一维元素序列,其中元素可以是任何IDL定义的类型。可以限制序列的最大长度。

    2.struct(结构)

    可以使用结构将多种类型的命名字段组装在一起

    每个struct都有两个构造函数。一个是默认的构造函数,把结构中的所有字段设置为空。第二个构造函数把结构字段作为参数并初始化每个字段

    3.union(联合)

     用于在任何给定的时间只引用几个数据成员中的一个(任何时间,内存中只能有一个成员)。联合使用discriminator标签值来显示该值含有联合中的那个成员。

    4. Any

     Any是一个保留其类型的自描述数据结构,它使你可以在运行时用类型安全的转换函数提取和插入预定义的IDL类型的值。

     Any类型让你指定一个属性值、参数或返回类型,该类型包含一个在运行时而不是在编译时确定的任意类型。可以使用Any传递任何东西。

     

     

     

    IDL-to-Java 的映射

    http://www.ibm.com/developerworks/cn/java/co-corbajct4/index.html

     

    http://www.ibm.com/developerworks/cn/java/co-cjct5/index.html

     

    Java IDL

    http://www.iplab.is.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/guide/idl/index.html

     




    展开全文
  • 第二节 定义接口

    千次阅读 2005-01-22 09:21:00
    第二节 定义接口 从技术上讲,接口是组包含了函数型方法的数据结构。通过组数据结构,客户代码可以调用组件对象的功能。 定义接口的一般形式为:[attributes] [modifiers] interface identifier [:base-list] {...

    第二节 定义接口


      从技术上讲,接口是一组包含了函数型方法的数据结构。通过这组数据结构,客户代码可以调用组件对象的功能。


      定义接口的一般形式为:
    [attributes] [modifiers] interface identifier [:base-list] {interface-body}[;]


      说明:


      1、attributes(可选):附加的定义性信息。


      2、modifiers(可选): 允许使用的修饰符有 new 和四个访问修饰符。分别是:new、public、protected、internal、 private。在一个接口定义中同一修饰符不允许出现多次,new 修饰符只能出现在嵌套接口中,表示覆盖了继承而来的同名成员。The public, protected, internal, and private 修饰符定义了对接口的访问权限。


      3、指示器和事件。


      4、identifier:接口名称。


      5、base-list(可选):包含一个或多个显式基接口的列表,接口间由逗号分隔。


      6、interface-body:对接口成员的定义。


    第四节、访问接口


      对接口成员的访问


      对接口方法的调用和采用索引指示器访问的规则与类中的情况也是相同的。如果底层成员的命名与继承而来的高层成员一致,那么底层成员将覆盖同名的高层成员。但由于接口支持多继承,在多继承中,如果两个父接口含有同名的成员,这就产生了二义性(这也正是C#中取消了类的多继承机制的原因之一),这时需要进行显式的定义:



    using System ;
    interface ISequence {
     int Count { get; set; }
    }
    interface IRing {
     void Count(int i) ;
    }
    interface IRingSequence: ISequence, IRing { }
     class CTest {
      void Test(IRingSequence rs) {
       //rs.Count(1) ; 错误, Count 有二义性
       //rs.Count = 1; 错误, Count 有二义性
       ((ISequence)rs).Count = 1; // 正确
       ((IRing)rs).Count(1) ; // 正确调用IRing.Count
      }
    }


      上面的例子中,前两条语句rs .Count(1)和rs .Count = 1会产生二义性,从而导致编译时错误,因此必须显式地给rs 指派父接口类型,这种指派在运行时不会带来额外的开销。


      再看下面的例子:


    using System ;
    interface IInteger {
     void Add(int i) ;
    }
    interface IDouble {
     void Add(double d) ;
    }
    interface INumber: IInteger, IDouble {}
     class CMyTest {
     void Test(INumber Num) {
      // Num.Add(1) ; 错误
      Num.Add(1.0) ; // 正确
      ((IInteger)n).Add(1) ; // 正确
      ((IDouble)n).Add(1) ; // 正确
     }
    }


      调用Num.Add(1) 会导致二义性,因为候选的重载方法的参数类型均适用。但是,调用Num.Add(1.0) 是允许的,因为1.0 是浮点数参数类型与方法IInteger.Add()的参数类型不一致,这时只有IDouble.Add 才是适用的。不过只要加入了显式的指派,就决不会产生二义性。


      接口的多重继承的问题也会带来成员访问上的问题。例如:


    interface IBase {
     void FWay(int i) ;
    }
    interface ILeft: IBase {
     new void FWay (int i) ;
    }
    interface IRight: IBase
    { void G( ) ; }
    interface IDerived: ILeft, IRight { }
    class CTest {
     void Test(IDerived d) {
      d. FWay (1) ; // 调用ILeft. FWay
      ((IBase)d). FWay (1) ; // 调用IBase. FWay
      ((ILeft)d). FWay (1) ; // 调用ILeft. FWay
      ((IRight)d). FWay (1) ; // 调用IBase. FWay
     }
    }


      上例中,方法IBase.FWay在派生的接口ILeft中被Ileft的成员方法FWay覆盖了。所以对d. FWay (1)的调用实际上调用了。虽然从IBase-> IRight-> IDerived这条继承路径上来看,ILeft.FWay方法是没有被覆盖的。我们只要记住这一点:一旦成员被覆盖以后,所有对其的访问都被覆盖以后的成员"拦截"了。


      7、接口可以是命名空间或类的成员,并且可以包含下列成员的签名: 方法、属性、索引器 。


      8、一个接口可从一个或多个基接口继承。

    接口这个概念在C#和Java中非常相似。接口的关键词是interface,一个接口可以扩展一个或者多个其他接口。按照惯例,接口的名字以大写字母"I"开头。下面的代码是C#接口的一个例子,它与Java中的接口完全一样:



    interface IShape {
     void Draw ( ) ;
    }


      如果你从两个或者两个以上的接口派生,父接口的名字列表用逗号分隔,如下面的代码所示:



    interface INewInterface: IParent1, IParent2 { }


      然而,与Java不同,C#中的接口不能包含域(Field)。另外还要注意,在C#中,接口内的所有方法默认都是公用方法。在Java中,方法定义可以带有public修饰符(即使这并非必要),但在C#中,显式为接口的方法指定public修饰符是非法的。例如,下面的C#接口将产生一个编译错误。



    interface IShape { public void Draw( ) ; }


      下面的例子定义了一个名为IControl 的接口,接口中包含一个成员方法Paint:



    interface IControl {
     void Paint( ) ;
    }


      在下例中,接口 IInterface从两个基接口 IBase1 和 IBase2 继承:



    interface IInterface: IBase1, IBase2 {
     void Method1( ) ;
     void Method2( ) ;
    }


      接口可由类实现。实现的接口的标识符出现在类的基列表中。例如:



    class Class1: Iface1, Iface2 {
     // class 成员。
    }


      类的基列表同时包含基类和接口时,列表中首先出现的是基类。例如:



    class ClassA: BaseClass, Iface1, Iface2 {
     // class成员。
    }


      以下的代码段定义接口IFace,它只有一个方法:



    interface IFace {
     void ShowMyFace( ) ;
    }


      不能从这个定义实例化一个对象,但可以从它派生一个类。因此,该类必须实现ShowMyFace抽象方法:



    class CFace:IFace
    {
     public void ShowMyFace( ) {
      Console.WriteLine(" implementation " ) ;
     }
    }

    基接口


      一个接口可以从零或多个接口继承,那些被称为这个接口的显式基接口。当一个接口有比零多的显式基接口时,那么在接口的定义中的形式为,接口标识符后面跟着由一个冒号":"和一个用逗号","分开的基接口标识符列表。


      接口基:


      :接口类型列表说明:


      1、一个接口的显式基接口必须至少同接口本身一样可访问。例如,在一个公共接口的基接口中指定一个私有或内部的接口是错误的。


      2、一个接口直接或间接地从它自己继承是错误的。


      3、接口的基接口都是显式基接口,并且是它们的基接口。换句话说,基接口的集合完全由显式基接口和它们的显式基接口等等组成。在下面的例子中
    interface IControl {
     void Paint( ) ;
    }
    interface ITextBox: IControl {
     void SetText(string text) ;
    }
    interface IListBox: IControl {
     void SetItems(string[] items) ;
    }
    interface IComboBox: ITextBox, IListBox { }


      IComboBox 的基接口是IControl, ITextBox, 和 IlistBox。


      4、一个接口继承它的基接口的所有成员。换句话说,上面的接口 IComboBox 就像Paint一样继承成员SetText 和 SetItems。


      5、一个实现了接口的类或结构也隐含地实现了所有接口的基接口。


      接口主体


      一个接口的接口主体定义接口的成员。



    interface-body:
    { interface-member-declarationsopt }


      定义接口主要是定义接口成员,请看下一节--定义接口成员。

    第三节 定义接口成员


      接口可以包含一个和多个成员,这些成员可以是方法、属性、索引指示器和事件,但不能是常量、域、操作符、构造函数或析构函数,而且不能包含任何静态成员。接口定义创建新的定义空间,并且接口定义直 接包含的接口成员定义将新成员引入该定义空间。


      说明:


      1、接口的成员是从基接口继承的成员和由接口本身定义的成员。


      2、接口定义可以定义零个或多个成员。接口的成员必须是方法、属性、事件或索引器。接口不能包含常数、字段、运算符、实例构造函数、析构函数或类型,也不能包含任何种类的静态成员。


      3、定义一个接口,该接口对于每种可能种类的成员都包含一个:方法、属性、事件和索引器。


      4、接口成员默认访问方式是public。接口成员定义不能包含任何修饰符,比如成员定义前不能加abstract,public,protected,internal,private,virtual,override 或static 修饰符。


      5、接口的成员之间不能相互同名。继承而来的成员不用再定义,但接口可以定义与继承而来的成员同名的成员,这时我们说接口成员覆盖了继承而来的成员,这不会导致错误,但编译器会给出一个警告。关闭警告提示的方式是在成员定义前加上一个new关键字。但如果没有覆盖父接口中的成员,使用new 关键字会导致编译器发出警告。


      6、方法的名称必须与同一接口中定义的所有属性和事件的名称不同。此外,方法的签名必须与同一接口中定义的所有其他方法的签名不同。


      7、属性或事件的名称必须与同一接口中定义的所有其他成员的名称不同。


      8、一个索引器的签名必须区别于在同一接口中定义的其他所有索引器的签名。


      9、接口方法声明中的属性(attributes), 返回类型(return-type), 标识符(identifier), 和形式参数列表(formal-parameter-lis)与一个类的方法声明中的那些有相同的意义。一个接口方法声明不允许指定一个方法主体,而声明通常用一个分号结束。


      10、接口属性声明的访问符与类属性声明的访问符相对应,除了访问符主体通常必须用分号。因此,无论属性是读写、只读或只写,访问符都完全确定。


      11、接口索引声明中的属性(attributes), 类型(type), 和形式参数列表 (formal-parameter-list)与类的索引声明的那些有相同的意义。


      下面例子中接口IMyTest包含了索引指示器、事件E、 方法F、 属性P 这些成员:


    interface IMyTest{
     string this[int index] { get; set; }
     event EventHandler E ;
     void F(int value) ;
     string P { get; set; }
    }
    public delegate void EventHandler(object sender, EventArgs e) ;


      下面例子中接口IStringList包含每个可能类型成员的接口:一个方法,一个属性,一个事件和一个索引。


    public delegate void StringListEvent(IStringList sender);
    public interface IStringList
    {
     void Add(string s);
     int Count { get; }
     event StringListEvent Changed;
     string this[int index] { get; set; }
    }


      接口成员的全权名


      使用接口成员也可采用全权名(fully qualified name)。接口的全权名称是这样构成的。接口名加小圆点"." 再跟成员名比如对于下面两个接口:


    interface IControl {
     void Paint( ) ;
    }
    interface ITextBox: IControl {
     void GetText(string text) ;
    }


      其中Paint 的全权名是IControl.Paint,GetText的全权名是ITextBox. GetText。当然,全权名中的成员名称必须是在接口中已经定义过的,比如使用ITextBox.Paint.就是不合理的。


      如果接口是名字空间的成员,全权名还必须包含名字空间的名称。


    namespace System
    {
     public interface IDataTable {
      object Clone( ) ;
     }
    }


      那么Clone方法的全权名是System. IDataTable.Clone。


      定义好了接口,接下来就是怎样访问接口,请看下一节--访问接口

    类对接口的实现


      前面我们已经说过,接口定义不包括方法的实现部分。接口可以通过类或结构来实现。我们主要讲述通过类来实现接口。用类来实现接口时,接口的名称必须包含在类定义中的基类列表中。


      下面的例子给出了由类来实现接口的例子。其中ISequence 为一个队列接口,提供了向队列尾部添加对象的成员方法Add( ),IRing 为一个循环表接口,提供了向环中插入对象的方法Insert(object obj),方法返回插入的位置。类RingSquence 实现了接口ISequence 和接口IRing。


    using System ;
    interface ISequence {
     object Add( ) ;
    }
    interface ISequence {
     object Add( ) ;
    }
    interface IRing {
     int Insert(object obj) ;
    }
    class RingSequence: ISequence, IRing
    {
     public object Add( ) {…}
     public int Insert(object obj) {…}
    }


      如果类实现了某个接口,类也隐式地继承了该接口的所有父接口,不管这些父接口有没有在类定义的基类表中列出。看下面的例子:


    using System ;
    interface IControl {
     void Paint( );
    }
    interface ITextBox: IControl {
     void SetText(string text);
    }
    interface IListBox: IControl {
     void SetItems(string[] items);
    }
    interface IComboBox: ITextBox, IListBox { }


      这里, 接口IcomboBox继承了ItextBox和IlistBox。类TextBox不仅实现了接口ITextBox,还实现了接口ITextBox 的父接口IControl。


      前面我们已经看到,一个类可以实现多个接口。再看下面的例子:


    interface IDataBound {
     void Bind(Binder b);
    }
    public class EditBox: Control, IControl, IDataBound {
     public void Paint( );
     public void Bind(Binder b) {...}
    }


      类EditBox从类Control中派生并且实现了Icontrol和IdataBound。在前面的例子中接口Icontrol中的Paint方法和IdataBound接口中的Bind方法都用类EditBox中的公共成员实现。C#提供一种实现这些方法的可选择的途径,这样可以使执行这些的类避免把这些成员设定为公共的。接口成员可以用有效的名称来实现。例如,类EditBox可以改作方法Icontrol.Paint和IdataBound.Bind来来实现。


    public class EditBox: IControl, IDataBound {
     void IControl.Paint( ) {...}
     void IDataBound.Bind(Binder b) {...}
    }


      因为通过外部指派接口成员实现了每个成员,所以用这种方法实现的成员称为外部接口成员。外部接口成员可以只是通过接口来调用。例如,Paint方法中EditBox的实现可以只是通过创建Icontrol接口来调用。


    class Test {
     static void Main( ) {
      EditBox editbox = new EditBox( );
      editbox.Paint( ); //错误: EditBox 没有Paint 事件
      IControl control = editbox;
      control.Paint( ); // 调用 EditBox的Paint事件
     }
    }


      上例中,类EditBox 从Control 类继承并同时实现了IControl and IDataBound 接口。EditBox 中的Paint 方法来自IControl 接口,Bind 方法来自IDataBound 接口,二者在EditBox 类中都作为公有成员实现。当然,在C# 中我们也可以选择不作为公有成员实现接口。


      如果每个成员都明显地指出了被实现的接口,通过这种途径被实现的接口我们称之为显式接口成员(explicit interface member)。 用这种方式我们改写上面的例子:


    public class EditBox: IControl, IDataBound {
     void IControl.Paint( ) {…}
     void IDataBound.Bind(Binder b) {…}
    }


      显式接口成员只能通过接口调用。例如:


    class CTest {
     static void Main( ) {
      EditBox editbox = new EditBox( ) ;
      editbox.Paint( ) ; //错误:不同的方法
      IControl control = editbox;
      control.Paint( ) ; //调用 EditBox的Paint方法
     }
    }


      上述代码中对editbox.Paint( )的调用是错误的,因为editbox 本身并没有提供这一方法。control.Paint( )是正确的调用方式。


      注释:接口本身不提供所定义的成员的实现,它仅仅说明这些成员,这些成员必须依靠实现接口的类或其它接口的支持。


      知道了怎样访问接口,我们还要知道怎样实现接口,要实现C#的接口,请看下一节-实现接口

    第五节、实现接口


      1、显式实现接口成员


      为了实现接口,类可以定义显式接口成员执行体(Explicit interface member implementations)。显式接口成员执行体可以是一个方法、一个属性、一个事件或者是一个索引指示器的定义,定义与该成员对应的全权名应保持一致。


    using System ;
    interface ICloneable {
     object Clone( ) ;
    }
    interface IComparable {
     int CompareTo(object other) ;
    }
    class ListEntry: ICloneable, IComparable {
     object ICloneable.Clone( ) {…}
     int IComparable.CompareTo(object other) {…}
    }


      上面的代码中ICloneable.Clone 和IComparable.CompareTo 就是显式接口成员执行体。


      说明:


      1、不能在方法调用、属性访问以及索引指示器访问中通过全权名访问显式接口成员执行体。事实上,显式接口成员执行体只能通过接口的实例,仅仅引用接口的成员名称来访问。


      2、显式接口成员执行体不能使用任何访问限制符,也不能加上abstract, virtual, override或static 修饰符。


      3、显式接口成员执行体和其他成员有着不同的访问方式。因为不能在方法调用、属性访问以及索引指示器访问中通过全权名访问,显式接口成员执行体在某种意义上是私有的。但它们又可以通过接口的实例访问,也具有一定的公有性质。


      4、只有类在定义时,把接口名写在了基类列表中,而且类中定义的全权名、类型和返回类型都与显式接口成员执行体完全一致时,显式接口成员执行体才是有效的,例如:


    class Shape: ICloneable {
    object ICloneable.Clone( ) {…}
    int IComparable.CompareTo(object other) {…}
    }

     使用显式接口成员执行体通常有两个目的:


      1、因为显式接口成员执行体不能通过类的实例进行访问,这就可以从公有接口中把接口的实现部分单独分离开。如果一个类只在内部使用该接口,而类的使用者不会直接使用到该接口,这种显式接口成员执行体就可以起到作用。


      2、显式接口成员执行体避免了接口成员之间因为同名而发生混淆。如果一个类希望对名称和返回类型相同的接口成员采用不同的实现方式,这就必须要使用到显式接口成员执行体。如果没有显式接口成员执行体,那么对于名称和返回类型不同的接口成员,类也无法进行实现。


      下面的定义是无效的,因为Shape 定义时基类列表中没有出现接口IComparable。


    class Shape: ICloneable
    {
    object ICloneable.Clone( ) {…}
    }
    class Ellipse: Shape
    {
    object ICloneable.Clone( ) {…}
    }


      在Ellipse 中定义ICloneable.Clone是错误的,因为Ellipse即使隐式地实现了接口ICloneable,ICloneable仍然没有显式地出现在Ellipse定义的基类列表中。


      接口成员的全权名必须对应在接口中定义的成员。如下面的例子中,Paint的显式接口成员执行体必须写成IControl.Paint。


    using System ;
    interface IControl
    {
     void Paint( ) ;
    }
    interface ITextBox: IControl
    {
     void SetText(string text) ;
    }
    class TextBox: ITextBox
    {
     void IControl.Paint( ) {…}
     void ITextBox.SetText(string text) {…}
    }


      实现接口的类可以显式实现该接口的成员。当显式实现某成员时,不能通过类实例访问该成员,而只能通过该接口的实例访问该成员。显式接口实现还允许程序员继承共享相同成员名的两个接口,并为每个接口成员提供一个单独的实现。


      下面例子中同时以公制单位和英制单位显示框的尺寸。Box类继承 IEnglishDimensions和 IMetricDimensions两个接口,它们表示不同的度量衡系统。两个接口有相同的成员名 Length 和 Width。


      程序清单1 DemonInterface.cs


    interface IEnglishDimensions {
    float Length ( ) ;
    float Width ( ) ;
    }
    interface IMetricDimensions {
    float Length ( ) ;
    float Width ( ) ;
    }
    class Box : IEnglishDimensions, IMetricDimensions {
    float lengthInches ;
    float widthInches ;
    public Box(float length, float width) {
    lengthInches = length ;
    widthInches = width ;
    }
    float IEnglishDimensions.Length( ) {
    return lengthInches ;
    }
    float IEnglishDimensions.Width( ) {
    return widthInches ;
    }
    float IMetricDimensions.Length( ) {
    return lengthInches * 2.54f ;
    }
    float IMetricDimensions.Width( ) {
    return widthInches * 2.54f ;
    }
    public static void Main( ) {
    //定义一个实类对象 "myBox"::
    Box myBox = new Box(30.0f, 20.0f);
    // 定义一个接口" eDimensions"::
    IEnglishDimensions eDimensions = (IEnglishDimensions) myBox;
    IMetricDimensions mDimensions = (IMetricDimensions) myBox;
    // 输出:
    System.Console.WriteLine(" Length(in): {0}", eDimensions.Length( ));
    System.Console.WriteLine(" Width (in): {0}", eDimensions.Width( ));
    System.Console.WriteLine(" Length(cm): {0}", mDimensions.Length( ));
    System.Console.WriteLine(" Width (cm): {0}", mDimensions.Width( ));
    }
    }


      输出:Length(in): 30,Width (in): 20,Length(cm): 76.2,Width (cm): 50.8


      代码讨论:如果希望默认度量采用英制单位,请正常实现 Length 和 Width 这两个方法,并从 IMetricDimensions 接口显式实现 Length 和 Width 方法:


    public float Length( ) {
    return lengthInches ;
    }
    public float Width( ){
    return widthInches;
    }
    float IMetricDimensions.Length( ) {
    return lengthInches * 2.54f ;
    }
    float IMetricDimensions.Width( ) {
    return widthInches * 2.54f ;
    }


      这种情况下,可以从类实例访问英制单位,而从接口实例访问公制单位:


    System.Console.WriteLine("Length(in): {0}", myBox.Length( )) ;
    System.Console.WriteLine("Width (in): {0}", myBox.Width( )) ;
    System.Console.WriteLine("Length(cm): {0}", mDimensions.Length( )) ;
    System.Console.WriteLine("Width (cm): {0}", mDimensions.Width( )) ;

    2、继承接口实现


      接口具有不变性,但这并不意味着接口不再发展。类似于类的继承性,接口也可以继承和发展。


      注意:接口继承和类继承不同,首先,类继承不仅是说明继承,而且也是实现继承;而接口继承只是说明继承。也就是说,派生类可以继承基类的方法实现,而派生的接口只继承了父接口的成员方法说明,而没有继承父接口的实现,其次,C#中类继承只允许单继承,但是接口继承允许多继承,一个子接口可以有多个父接口。


      接口可以从零或多个接口中继承。从多个接口中继承时,用":"后跟被继承的接口名字,多个接口名之间用","分割。被继承的接口应该是可以访问得到的,比如从private 类型或internal 类型的接口中继承就是不允许的。接口不允许直接或间接地从自身继承。和类的继承相似,接口的继承也形成接口之间的层次结构。


      请看下面的例子:


    using System ;
    interface IControl {
    void Paint( ) ;
    }
    interface ITextBox: IControl {
    void SetText(string text) ;
    }
    interface IListBox: IControl {
    void SetItems(string[] items) ;
    }
    interface IComboBox: ITextBox, IListBox { }


      对一个接口的继承也就继承了接口的所有成员,上面的例子中接口ITextBox和IListBox都从接口IControl中继承,也就继承了接口IControl的Paint方法。接口IComboBox从接口ITextBox和IListBox中继承,因此它应该继承了接口ITextBox的SetText方法和IListBox的SetItems方法,还有IControl的Paint方法。
    一个类继承了所有被它的基本类提供的接口实现程序。


      不通过显式的实现一个接口,一个派生类不能用任何方法改变它从它的基本类继承的接口映射。例如,在声明中


    interface IControl {
    void Paint( );
    }
    class Control: IControl {
    public void Paint( ) {...}
    }
    class TextBox: Control {
    new public void Paint( ) {...}
    }


      TextBox 中的方法Paint 隐藏了Control中的方法Paint ,但是没有改变从Control.Paint 到IControl.Paint 的映射,而通过类实例和接口实例调用Paint将会有下面的影响


    Control c = new Control( ) ;
    TextBox t = new TextBox( ) ;
    IControl ic = c ;
    IControl it = t ;
    c.Paint( ) ; // 影响Control.Paint( ) ;
    t.Paint( ) ; // 影响TextBox.Paint( ) ;
    ic.Paint( ) ; // 影响Control.Paint( ) ;
    it.Paint( ) ; // 影响Control.Paint( ) ;


      但是,当一个接口方法被映射到一个类中的虚拟方法,派生类就不可能覆盖这个虚拟方法并且改变接口的实现函数。例如,把上面的声明重新写为


    interface IControl {
    void Paint( ) ;
    }
    class Control: IControl {
    public virtual void Paint( ) {...}
    }
    class TextBox: Control {
    public override void Paint( ) {...}
    }


      就会看到下面的结果:


    Control c = new Control( ) ;
    TextBox t = new TextBox( ) ;
    IControl ic = c ;
    IControl it = t ;
    c.Paint( ) ; // 影响Control.Paint( );
    t.Paint( ) ; // 影响TextBox.Paint( );
    ic.Paint( ) ; // 影响Control.Paint( );
    it.Paint( ) ; // 影响TextBox.Paint( );


      由于显式接口成员实现程序不能被声明为虚拟的,就不可能覆盖一个显式接口成员实现程序。一个显式接口成员实现程序调用另外一个方法是有效的,而另外的那个方法可以被声明为虚拟的以便让派生类可以覆盖它。例如:


    interface IControl {
     void Paint( ) ;
    }
    class Control: IControl {
     void IControl.Paint( ) { PaintControl( ); }
     protected virtual void PaintControl( ) {...}
    }
    class TextBox: Control {
     protected override void PaintControl( ) {...}
    }


      这里,从Control 继承的类可以通过覆盖方法PaintControl 来对IControl.Paint 的实现程序进行特殊化。

    3、重新实现接口


      我们已经介绍过,派生类可以对基类中已经定义的成员方法进行重载。类似的概念引入到类对接口的实现中来,叫做接口的重实现(re-implementation)。继承了接口实现的类可以对接口进行重实现。这个接口要求是在类定义的基类列表中出现过的。对接口的重实现也必须严格地遵守首次实现接口的规则,派生的接口映射不会对为接口的重实现所建立的接口映射产生任何影响。


      下面的代码给出了接口重实现的例子:


    interface IControl {
     void Paint( ) ;
     class Control: IControl
     void IControl.Paint( ) {…}
     class MyControl: Control, IControl
     public void Paint( ) {}
    }


      实际上就是:Control把IControl.Paint映射到了Control.IControl.Paint上,但这并不影响在MyControl中的重实现。在MyControl中的重实现中,IControl.Paint被映射到MyControl.Paint 之上。


      在接口的重实现时,继承而来的公有成员定义和继承而来的显式接口成员的定义参与到接口映射的过程。


    using System ;
    interface IMethods {
     void F( ) ;
     void G( ) ;
     void H( ) ;
     void I( ) ;
    }
    class Base: IMethods {
     void IMethods.F( ) { }
     void IMethods.G( ) { }
     public void H( ) { }
     public void I( ) { }
    }
    class Derived: Base, IMethods {
     public void F( ) { }
     void IMethods.H( ) { }
    }


      这里,接口IMethods在Derived中的实现把接口方法映射到了Derived.F,Base.IMethods.G, Derived.IMethods.H, 还有Base.I。前面我们说过,类在实现一个接口时,同时隐式地实现了该接口的所有父接口。同样,类在重实现一个接口时同时,隐式地重实现了该接口的所有父接口。


    using System ;
    interface IBase {
     void F( ) ;
    }
    interface IDerived: IBase {
     void G( ) ;
    }
    class C: IDerived {
     void IBase.F( ) {
     //对F 进行实现的代码…
    }
    void IDerived.G( ) {
     //对G 进行实现的代码…
    }
    }
    class D: C, IDerived {
     public void F( ) {
     //对F 进行实现的代码…
    }
    public void G( ) {
     //对G 进行实现的代码…
    }
    }


      这里,对IDerived的重实现也同样实现了对IBase的重实现,把IBase.F 映射到了D.F

    4、映射接口


      类必须为在基类表中列出的所有接口的成员提供具体的实现。在类中定位接口成员的实现称之为接口映射(interface mapping )。


      映射,数学上表示一一对应的函数关系。接口映射的含义也是一样,接口通过类来实现,那么对于在接口中定义的每一个成员,都应该对应着类的一个成员来为它提供具体的实现。


      类的成员及其所映射的接口成员之间必须满足下列条件:


      1、如果A和B都是成员方法,那么A和B的名称、类型、形参表(包括参数个数和每一个参数的类型)都应该是一致的。


      2、如果A和B都是属性,那么A和B的名称、类型应当一致,而且A和B的访问器也是类似的。但如果A不是显式接口成员执行体,A允许增加自己的访问器。


      3、如果A和B都是时间那么A和B的名称、类型应当一致。


      4、如果A和B都是索引指示器,那么A和B的类型、形参表(包括参数个数和每一个参数的类型)应当一致。而且A和B的访问器也是类似的。但如果A不是显式接口成员执行体,A允许增加自己的访问器。


      那么,对于一个接口成员,怎样确定由哪一个类的成员来实现呢?即一个接口成员映射的是哪一个类的成员?在这里,我们叙述一下接口映射的过程。假设类C实现了一个接口IInterface,Member是接口IInterface中的一个成员,在定位由谁来实现接口成员Member,即Member的映射过程是这样的:


      1、如果C中存在着一个显式接口成员执行体,该执行体与接口IInterface 及其成员Member相对应,则由它来实现Member 成员。


      2、如果条件(1)不满足,且C中存在着一个非静态的公有成员,该成员与接口成员Member相对应,则由它来实现Member 成员。


      3、如果上述条件仍不满足,则在类C定义的基类列表中寻找一个C 的基类D,用D来代替C。


      4、重复步骤1-- 3 ,遍历C的所有直接基类和非直接基类,直到找到一个满足条件的类的成员。


      5、如果仍然没有找到,则报告错误。


      下面是一个调用基类方法来实现接口成员的例子。类Class2 实现了接口Interface1,类Class2 的基类Class1 的成员也参与了接口的映射,也就是说类Class2 在对接口Interface1进行实现时,使用了类Class1提供的成员方法F来实现接口Interface1的成员方法F:


    interface Interface1 {
     void F( ) ;
    }
    class Class1 {
     public void F( ) { }
     public void G( ) { }
    }
    class Class2: Class1, Interface1 {
     new public void G( ) {}
    }


      注意:接口的成员包括它自己定义的成员,而且包括该接口所有父接口定义的成员。在接口映射时,不仅要对接口定义体中显式定义的所有成员进行映射,而且要对隐式地从父接口那里继承来的所有接口成员进行映射。

    在进行接口映射时,还要注意下面两点:


      1、在决定由类中的哪个成员来实现接口成员时,类中显式说明的接口成员比其它成员优先实现。


      2、使用Private、protected和static修饰符的成员不能参与实现接口映射。例如:


    interface ICloneable {
     object Clone( ) ;
    }
    class C: ICloneable {
     object ICloneable.Clone( ) {…}
     public object Clone( ) {…}
    }


      例子中成员ICloneable.Clone 称为接口ICloneable 的成员Clone 的实现者,因为它是显式说明的接口成员,比其它成员有着更高的优先权。


      如果一个类实现了两个或两个以上名字、类型和参数类型都相同的接口,那么类中的一个成员就可能实现所有这些接口成员:


    interface IControl {
     void Paint( ) ;
    }
    interface IForm {
     void Paint( ) ;
    }
    class Page: IControl, IForm {
     public void Paint( ) {…}
    }


      这里,接口IControl和IForm的方法Paint都映射到了类Page中的Paint方法。当然也可以分别用显式的接口成员分别实现这两个方法:


    interface IControl {
     void Paint( ) ;
    }
    interface IForm {
     void Paint( ) ;
    }
    class Page: IControl, IForm {
     public void IControl.Paint( ) {
     //具体的接口实现代码
    }
    public void IForm.Paint( ) {
     //具体的接口实现代码
    }
    }


      上面的两种写法都是正确的。但是如果接口成员在继承中覆盖了父接口的成员,那么对该接口成员的实现就可能必须映射到显式接口成员执行体。看下面的例子:


    interface IBase {
     int P { get; }
    }
    interface IDerived: IBase {
     new int P( ) ;
    }


      接口IDerived从接口IBase中继承,这时接口IDerived 的成员方法覆盖了父接口的成员方法。因为这时存在着同名的两个接口成员,那么对这两个接口成员的实现如果不采用显式接口成员执行体,编译器将无法分辨接口映射。所以,如果某个类要实现接口IDerived,在类中必须至少定义一个显式接口成员执行体。采用下面这些写法都是合理的:


    //一:对两个接口成员都采用显式接口成员执行体来实现
    lass C: IDerived {
     int IBase.P
     get
     { //具体的接口实现代码 }
      int IDerived.P( ){
      //具体的接口实现代码 }
     }
    //二:对Ibase 的接口成员采用显式接口成员执行体来实现
    class C: IDerived {
     int IBase.P
     get {//具体的接口实现代码}
      public int P( ){
      //具体的接口实现代码 }
     }
    //三:对IDerived 的接口成员采用显式接口成员执行体来实现
    class C: IDerived{
     public int P
     get {//具体的接口实现代码}
     int IDerived.P( ){
     //具体的接口实现代码}
    }


      另一种情况是,如果一个类实现了多个接口,这些接口又拥有同一个父接口,这个父接口只允许被实现一次。


    using System ;
    interface IControl {
     void Paint( ) ;
     interface ITextBox: IControl {
     void SetText(string text) ;
    }
    interface IListBox: IControl {
     void SetItems(string[] items) ;
    }
    class ComboBox: IControl, ITextBox, IListBox {
     void IControl.Paint( ) {…}
     void ITextBox.SetText(string text) {…}
     void IListBox.SetItems(string[] items) {…}
    }


      上面的例子中,类ComboBox实现了三个接口:IControl,ITextBox和IListBox。如果认为ComboBox不仅实现了IControl接口,而且在实现ITextBox和IListBox的同时,又分别实现了它们的父接口IControl。实际上,对接口ITextBox 和IListBox 的实现,分享了对接口IControl 的实现。


      我们对C#的接口有了较全面的认识,基本掌握了怎样应用C#的接口编程,但事实上,C#的不仅仅应用于.NET平台,它同样支持以前的COM,可以实现COM类到.NET类的转换,如C#调用API。欲了解这方面的知识,请看下一节-接口转换。

    第六节、接口转换


      C#中不仅支持.Net 平台,而且支持COM平台。为了支持 COM和.Net,C# 包含一种称为属性的独特语言特性。一个属性实际上就是一个 C# 类,它通过修饰源代码来提供元信息。属性使 C# 能够支持特定的技术,如 COM 和 .Net,而不会干扰语言规范本身。C# 提供将COM接口转换为 C#接口的属性类。另一些属性类将 COM类转换为C# 类。执行这些转换不需要任何 IDL 或类工厂。


      现在部署的任何COM 组件都可以在接口转换中使用。通常情况下,所需的调整是完全自动进行的。


      特别是,可以使用运行时可调用包装 (RCW) 从 .NET 框架访问 COM 组件。此包装将 COM 组件提供的 COM 接口转换为与 .NET 框架兼容的接口。对于 OLE 自动化接口,RCW 可以从类型库中自动生成;对于非 OLE 自动化接口,开发人员可以编写自定义 RCW,手动将 COM 接口提供的类型映射为与 .NET 框架兼容的类型。


      使用ComImport引用COM组件
    COM Interop 提供对现有 COM 组件的访问,而不需要修改原始组件。使用ComImport引用COM组件常包括下面 几个方面的问题:


      1、创建 COM 对象。


      2、确定 COM 接口是否由对象实现。


      3、调用 COM 接口上的方法。


      4、实现可由 COM 客户端调用的对象和接口。


      创建 COM 类包装


      要使 C# 代码引用COM 对象和接口,需要在 C# 中包含 COM 接口的定义。完成此操作的最简单方法是使用 TlbImp.exe(类型库导入程序),它是一个包括在 .NET 框架 SDK 中的命令行工具。TlbImp 将 COM 类型库转换为 .NET 框架元数据,从而有效地创建一个可以从任何托管语言调用的托管包装。用 TlbImp 创建的 .NET 框架元数据可以通过 /R 编译器选项包括在 C# 内部版本中。如果使用 Visual Studio 开发环境,则只需添加对 COM 类型库的引用,将为您自动完成此转换。


      TlbImp 执行下列转换:


      1、COM coclass 转换为具有无参数构造函数的 C# 类。


      2、COM 结构转换为具有公共字段的 C# 结构。


      检查 TlbImp 输出的一种很好的方法是运行 .NET 框架 SDK 命令行工具 Ildasm.exe(Microsoft 中间语言反汇编程序)来查看转换结果。


      虽然 TlbImp 是将 COM 定义转换为 C# 的首选方法,但也不是任何时候都可以使用它(例如,在没有 COM 定义的类型库时或者 TlbImp 无法处理类型库中的定义时,就不能使用该方法)。在这些情况下,另一种方法是使用 C# 属性在 C# 源代码中手动定义 COM 定义。创建 C# 源映射后,只需编译 C# 源代码就可产生托管包装。


      执行 COM 映射需要理解的主要属性包括:


      1、ComImport:它将类标记为在外部实现的 COM 类。


      2、Guid:它用于为类或接口指定通用唯一标识符 (UUID)。


      3、InterfaceType,它指定接口是从 IUnknown 还是从 IDispatch 派生。


      4、PreserveSig,它指定是否应将本机返回值从 HRESULT 转换为 .NET 框架异常。
      声明 COM coclass


      COM coclass 在 C# 中表示为类。这些类必须具有与其关联的 ComImport 属性。下列限制适用于这些类:


      1、类不能从任何其他类继承。


      2、类不能实现任何接口。


      4、类还必须具有为其设置全局唯一标识符 (GUID) 的 Guid 属性。


      以下示例在 C# 中声明一个 coclass:


    // 声明一个COM类 FilgraphManager
    [ComImport, Guid("E436EBB3-524F-11CE-9F53-0020AF0BA770")]
    class FilgraphManager
    { }


      C# 编译器将添加一个无参数构造函数,可以调用此构造函数来创建 COM coclass 的实例。


      创建 COM 对象


      COM coclass 在 C# 中表示为具有无参数构造函数的类。使用 new 运算符创建该类的实例等效于在 C# 中调用 CoCreateInstance。使用以上定义的类,就可以很容易地实例化此类:


    class MainClass
    {
    public static void Main()
    {
    FilgraphManager filg = new FilgraphManager();
    }
    }

      声明 COM 接口


      COM 接口在 C# 中表示为具有 ComImport 和 Guid 属性的接口。它不能在其基接口列表中包含任何接口,而且必须按照方法在 COM 接口中出现的顺序声明接口成员函数。


      在 C# 中声明的 COM 接口必须包含其基接口的所有成员的声明,IUnknown 和 IDispatch 的成员除外(.NET 框架将自动添加这些成员)。从 IDispatch 派生的 COM 接口必须用 InterfaceType 属性予以标记。
    从 C# 代码调用 COM 接口方法时,公共语言运行库必须封送与 COM 对象之间传递的参数和返回值。对于每个 .NET 框架类型均有一个默认类型,公共语言运行库将使用此默认类型在 COM 调用间进行封送处理时封送。例如,C# 字符串值的默认封送处理是封送到本机类型 LPTSTR(指向 TCHAR 字符缓冲区的指针)。可以在 COM 接口的 C# 声明中使用 MarshalAs 属性重写默认封送处理。


      在 COM 中,返回成功或失败的常用方法是返回一个 HRESULT,并在 MIDL 中有一个标记为"retval"、用于方法的实际返回值的 out 参数。在 C#(和 .NET 框架)中,指示已经发生错误的标准方法是引发异常。
    默认情况下,.NET 框架为由其调用的 COM 接口方法在两种异常处理类型之间提供自动映射。


      返回值更改为标记为 retval 的参数的签名(如果方法没有标记为 retval 的参数,则为 void)。


      标记为 retval 的参数从方法的参数列表中剥离。


      任何非成功返回值都将导致引发 System.COMException 异常。


      此示例显示用 MIDL 声明的 COM 接口以及用 C# 声明的同一接口(注意这些方法使用 COM 错误处理方法)。


      下面是接口转换的C#程序:


    using System.Runtime.InteropServices;
    // 声明一个COM接口 IMediaControl
    [Guid("56A868B1-0AD4-11CE-B03A-0020AF0BA770"),
    InterfaceType(ComInterfaceType.InterfaceIsDual)]
    interface IMediaControl // 这里不能列出任何基接口
    {
    void Run();
    void Pause();
    void Stop();
    void GetState( [In] int msTimeout, [Out] out int pfs);
    void RenderFile(
    [In, MarshalAs(UnmanagedType.BStr)] string strFilename);
    void AddSourceFilter(
    [In, MarshalAs(UnmanagedType.BStr)] string strFilename,
    [Out, MarshalAs(UnmanagedType.Interface)] out object ppUnk);
    [return : MarshalAs(UnmanagedType.Interface)]
    object FilterCollection();
    [return : MarshalAs(UnmanagedType.Interface)]
    object RegFilterCollection();
    void StopWhenReady();
    }


      若要防止 HRESULT 翻译为 COMException,请在 C# 声明中将 PreserveSig(true) 属性附加到方法。
      下面是一个使用C# 映射媒体播放机COM 对象的程序。


      程序清单2 DemonCOM.cs


    using System;
    using System.Runtime.InteropServices;
    namespace QuartzTypeLib
    {
    //声明一个COM接口 IMediaControl,此接口来源于媒体播放机COM类
    [Guid("56A868B1-0AD4-11CE-B03A-0020AF0BA770"),
    InterfaceType(ComInterfaceType.InterfaceIsDual)]
    interface IMediaControl
    { //列出接口成员
    void Run();
    void Pause();
    void Stop();
    void GetState( [In] int msTimeout, [Out] out int pfs);
    void RenderFile(
    [In, MarshalAs(UnmanagedType.BStr)] string strFilename);
    void AddSourceFilter(
    [In, MarshalAs(UnmanagedType.BStr)] string strFilename,
    [Out, MarshalAs(UnmanagedType.Interface)]
    out object ppUnk);
    [return: MarshalAs(UnmanagedType.Interface)]
    object FilterCollection();
    [return: MarshalAs(UnmanagedType.Interface)]
    object RegFilterCollection();
    void StopWhenReady();
    }
    //声明一个COM类:
    [ComImport, Guid("E436EBB3-524F-11CE-9F53-0020AF0BA770")]
    class FilgraphManager //此类不能再继承其它基类或接口
    {
    //这里不能有任何代码 ,系统自动增加一个缺省的构造函数
    }
    }
    class MainClass
    {
    public static void Main(string[] args)
    {
    //命令行参数:
    if (args.Length != 1)
    {
    DisplayUsage();
    return;
    }
    String filename = args[0];
    if (filename.Equals("/?"))
    {
    DisplayUsage();
    return;
    }
    // 声明FilgraphManager的实类对象:
    QuartzTypeLib.FilgraphManager graphManager =new QuartzTypeLib.FilgraphManager();
    //声明IMediaControl的实类对象::
    QuartzTypeLib.IMediaControl mc =(QuartzTypeLib.IMediaControl)graphManager;
    // 调用COM的方法:
    mc.RenderFile(filename);
    //运行文件.
    mc.Run();
    //暂借停.
    Console.WriteLine("Press Enter to continue.");
    Console.ReadLine();
    }
    private static void DisplayUsage()
    { // 显示
    Console.WriteLine("媒体播放机: 播放 AVI 文件.");
    Console.WriteLine("使用方法: VIDEOPLAYER.EXE 文件名");
    }
    }

    运行示例:


      若要显示影片示例 Clock.avi,请使用以下命令:


    interop2 %windir%/clock.avi


      这将在屏幕上显示影片,直到按 ENTER 键停止。
      在 .NET 框架程序中通过DllImport使用 Win32 API


      .NET 框架程序可以通过静态 DLL 入口点的方式来访问本机代码库。DllImport 属性用于指定包含外部方法的实现的dll 位置。


      DllImport 属性定义如下:


    namespace System.Runtime.InteropServices
    {
     [AttributeUsage(AttributeTargets.Method)]
     public class DllImportAttribute: System.Attribute
     {
      public DllImportAttribute(string dllName) {...}
      public CallingConvention CallingConvention;
      public CharSet CharSet;
      public string EntryPoint;
      public bool ExactSpelling;
      public bool PreserveSig;
      public bool SetLastError;
      public string Value { get {...} }
     }
    }


      说明:


      1、DllImport只能放置在方法声明上。


      2、DllImport具有单个定位参数:指定包含被导入方法的 dll 名称的 dllName 参数。


      3、DllImport具有五个命名参数:


       a、CallingConvention 参数指示入口点的调用约定。如果未指定 CallingConvention,则使用默认值 CallingConvention.Winapi。


       b、CharSet 参数指示用在入口点中的字符集。如果未指定 CharSet,则使用默认值 CharSet.Auto。


       c、EntryPoint 参数给出 dll 中入口点的名称。如果未指定 EntryPoint,则使用方法本身的名称。


       d、ExactSpelling 参数指示 EntryPoint 是否必须与指示的入口点的拼写完全匹配。如果未指定 ExactSpelling,则使用默认值 false。


       e、PreserveSig 参数指示方法的签名应当被保留还是被转换。当签名被转换时,它被转换为一个具有 HRESULT 返回值和该返回值的一个名为 retval 的附加输出参数的签名。如果未指定 PreserveSig,则使用默认值 true。


       f、SetLastError 参数指示方法是否保留 Win32"上一错误"。如果未指定 SetLastError,则使用默认值 false。


      4、它是一次性属性类。


      5、此外,用 DllImport 属性修饰的方法必须具有 extern 修饰符。


      下面是 C# 调用 Win32 MessageBox 函数的示例:


    using System;
    using System.Runtime.InteropServices;
    class MainApp
    { //通过DllImport引用user32.dll类。MessageBox来自于user32.dll类
     [DllImport("user32.dll", EntryPoint="MessageBox")]
     public static extern int MessageBox(int hWnd, String strMessage, String strCaption, uint uiType);
     public static void Main()
     {
      MessageBox( 0, "您好,这是 PInvoke!", ".NET", 0 );
     }
    }


      面向对象的编程语言几乎都用到了抽象类这一概念,抽象类为实现抽象事物提供了更大的灵活性。C#也不例外, C#通过覆盖虚接口的技术深化了抽象类的应用。欲了解这方面的知识,请看下一节-覆盖虚接口

    第七节、覆盖虚接口


      有时候我们需要表达一种抽象的东西,它是一些东西的概括,但我们又不能真正的看到它成为一个实体在我们眼前出现,为此面向对象的编程语言便有了抽象类的概念。C#作为一个面向对象的语言,必然也会引入抽象类这一概念。接口和抽象类使您可以创建组件交互的定义。通过接口,可以指定组件必须实现的方法,但不实际指定如何实现方法。抽象类使您可以创建行为的定义,同时提供用于继承类的一些公共实现。对于在组件中实现多态行为,接口和抽象类都是很有用的工具。


      一个抽象类必须为类的基本类列表中列出的接口的所有成员提供实现程序。但是,一个抽象类被允许把接口方法映射到抽象方法中。例如



    interface IMethods {
     void F();
     void G();
    }
    abstract class C: IMethods
    {
     public abstract void F();
     public abstract void G();
    }


      这里, IMethods 的实现函数把F和G映射到抽象方法中,它们必须在从C派生的非抽象类中被覆盖。
    注意显式接口成员实现函数不能是抽象的,但是显式接口成员实现函数当然可以调用抽象方法。例如



    interface IMethods
    {
     void F();
     void G();
    }
    abstract class C: IMethods
    {
     void IMethods.F() { FF(); }
     void IMethods.G() { GG(); }
     protected abstract void FF();
     protected abstract void GG();
    }


      这里,从C派生的非抽象类要覆盖FF 和 GG, 因此提供了IMethods的实际实现程序。

    展开全文
  • 1、Throwable接口中的getStackTrace()方法(或者Thread类的getStackTrace()方法),根据这个方法可以得到函数的逐层调用地址,其返回值为StackTraceElement[];2、StackTraceElement类,其中四个方法getClassName()...

    1、Throwable接口中的getStackTrace()方法(或者Thread类的getStackTrace()方法),根据这个方法可以得到函数的逐层调用地址,其返回值为StackTraceElement[];

    2、StackTraceElement类,其中四个方法getClassName(),getFileName(),getLineNumber(),getMethodName()在调试程序打印Log时非常有用;

    3、UncaughtExceptionHandler接口,再好的代码异常难免,利用此接口可以对未捕获的异常善后;

    使用参见:Android使用UncaughtExceptionHandler捕获全局异常

    4、Resources类中的getIdentifier(name, defType, defPackage)方法,根据资源名称获取其ID,做UI时经常用到;

    5、View中的isShown()方法,以前都是用view.getVisibility() == View.VISIBLE来判断的(╯□╰);(谢评论提醒,这里面其实有一个坑:【android】view.isShown ()的用法)

    6、Arrays类中的一系列关于数组操作的工具方法:binarySearch(),asList(),equals(),sort(),toString(),copyOfRange()等;

    Collections类中的一系列关于集合操作的工具方法:sort(),reverse()等;

    7、android.text.format.Formatter类中formatFileSize(Context, long)方法,用来格式化文件Size(B → KB → MB → GB);

    8、android.media.ThumbnailUtils类,用来获取媒体(图片、视频)缩略图;

    9、String类中的format(String, Object…)方法,用来格式化strings.xml中的字符串(多谢@droider An提示:Context类中getString(int, Object… )方法用起来更加方便);

    10、View类中的三个方法:callOnClick(),performClick(),performLongClick(),用于触发View的点击事件;

    11、TextUtils类中的isEmpty(CharSequence)方法,判断字符串是否为null或”“;

    12、TextView类中的append(CharSequence)方法,添加文本。一些特殊文本直接用+连接会变成String;

    13、View类中的getDrawingCache()等一系列方法,目前只知道可以用来截图;

    14、DecimalFormat类,用于字串格式化包括指定位数、百分数、科学计数法等;

    15、System类中的arraycopy(src, srcPos, dest, destPos,length)方法,用来copy数组;

    16、Fragment类中的onHiddenChanged(boolean)方法,使用FragmentTransaction中的hide(),show()时貌似Fragment的其它生命周期方法都不会被调用,太坑爹!

    17、Activity类中的onWindowFocusChanged(boolean),onNewIntent(intent)等回调方法;

    18、View类中的getLocationInWindow(int[])方法和getLocationOnScreen(int[])方法,获取View在窗口/屏幕中的位置;

    19、TextView类中的setTransformationMethod(TransformationMethod)方法,可用来实现“显示密码”功能;

    20、TextWatcher接口,用来监听文本输入框内容的改变,可用来实现一系列具有特殊功能的文本输入框;

    21、View类中的setSelected(boolean)方法结合android:state_selected=”“用来实现图片选中效果;

    22、Surface设置透明:SurfaceView.setZOrderOnTop(true);

    SurfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT);但是会挡住其它控件;

    23、ListView或GridView类中的setFastScrollEnabled(boolean)方法,用来设置快速滚动滑块是否可见,当然前提是item够多;

    24、PageTransformer接口,用来自定义ViewPager页面切换动画,用setPageTransformer(boolean, PageTransformer)方法来进行设置;

    25、apache提供的一系列jar包:commons-lang.jar,commons-collections.jar,commons-beanutils.jar等,里面很多方法可能是你曾经用几十几百行代码实现过的,但是执行效率或许要差很多,比如:ArrayUtils,StringUtils……;

    26、AndroidTestCase类,Android单元测试,在AndroidStudio中使用非常方便;

    27、TextView类的setKeyListener(KeyListener)方法;

    其中DigitsKeyListener类,使用getInstance(String accepted)方法即可指定EditText可输入字符集;

    28、ActivityLifecycleCallbacks接口,用于在Application类中监听各Activity的状态变化;

    29、Context类中的createPackageContext(packageName, flags)方法,可用来获取指定包名应用程序的Context对象。

    Part 1:

    Activity.startActivities()常用于在应用程序中间启动其他的Activity。

    TextUtils.isEmpty()简单的工具类,用于检测是否为空。

    Html.fromHtml()用于生成一个Html,参数可以是一个字符串.个人认为它不是很快,所以我不怎么经常去用.(我说不经常用它是为了重点突出这句话:请多手动构建 Spannable 来替换 Html.fromHtml),但是它对渲染从 web 上获取的文字还是很不错的。

    TextView.setError()在验证用户输入的时候很棒。

    Build.VERSION_CODES这个标明了当前的版本号,在处理兼容性问题的时候经常会用到.点进去可以看到各个版本的不同特性。

    Log.getStackTraceString()方便的日志类工具,方法Log.v()、Log.d()、Log.i()、Log.w()和Log.e()都是将信息打印到LogCat中,有时候需要将出错的信息插入到数据库或一个自定义的日志文件中,那么这种情况就需要将出错的信息以字符串的形式返回来,也就是使用static String getStackTraceString(Throwable tr)方法的时候。

    LayoutInflater.from()顾名思义,用于Inflate一个layout,参数是layout的id.这个经常写Adapter的人会用的比较多。

    ViewConfiguration.getScaledTouchSlop()使用 ViewConfiguration 中提供的值以保证所有触摸的交互都是统一的。这个方法获取的值表示:用户的手滑动这个距离后,才判定为正在进行滑动.当然这个值也可以自己来决定.但是为了一致性,还是使用标准的值较好。

    PhoneNumberUtils.convertKeypadLettersToDigits顾名思义.将字母转换为数字,类似于T9输入法,

    Context.getCacheDir()获取缓存数据文件夹的路径,很简单但是知道的人不多,这个路径通常在SD卡上(这里的SD卡指的是广义上的SD卡,包括外部存储和内部存储)Adnroid/data/您的应用程序包名/cache/ 下面.测试的时候,可以去这里面看是否缓存成功.缓存在这里的好处是:不用自己再去手动创建文件夹,不用担心用户把自己创建的文件夹删掉,在应用程序卸载的时候,这里会被清空,使用第三方的清理工具的时候,这里也会被清空。

    ArgbEvaluator用于处理颜色的渐变。就像 Chris Banes 说的一样,这个类会进行很多自动装箱的操作,所以最好还是去掉它的逻辑自己去实现它。这个没用过,不明其所以然,回头再补充.

    ContextThemeWrapper方便在运行的时候修改主题。

    Spacespace是Android 4.0中新增的一个控件,它实际上可以用来分隔不同的控件,其中形成一个空白的区域.这是一个轻量级的视图组件,它可以跳过Draw,对于需要占位符的任何场景来说都是很棒的。

    ValueAnimator.reverse()这个方法可以很顺利地取消正在运行的动画。

    Part 2:

    DateUtils.formatDateTime()用来进行区域格式化工作,输出格式化和本地化的时间或者日期。

    AlarmManager.setInexactRepeating通过闹铃分组的方式省电,即使你只调用了一个闹钟,这也是一个好的选择,(可以确保在使用完毕时自动调用 AlarmManager.cancel ()。原文说的比较抽象,这里详细说一下:setInexactRepeating指的是设置非准确闹钟,使用方法:alarmManager.setInexactRepeating(AlarmManager.RTC, startTime,intervalL, pendingIntent),非准确闹钟只能保证大致的时间间隔,但是不一定准确,可能出现设置间隔为30分钟,但是实际上一次间隔20分钟,另一次间隔40分钟。它的最大的好处是可以合并闹钟事件,比如间隔设置每30分钟一次,不唤醒休眠,在休眠8小时后已经积累了16个闹钟事件,而在手机被唤醒的时候,非准时闹钟可以把16个事件合并为一个, 所以这么看来,非准时闹钟一般来说比较节约能源。

    Formatter.formatFileSize()一个区域化的文件大小格式化工具。通俗来说就是把大小转换为MB,G,KB之类的字符串。

    ActionBar.hide()/.show()顾名思义,隐藏和显示ActionBar,可以优雅地在全屏和带Actionbar之间转换。

    Linkify.addLinks()在Text上添加链接。很实用。

    StaticLayout在自定义 View 中渲染文字的时候很实用。

    Activity.onBackPressed()很方便的管理back键的方法,有时候需要自己控制返回键的事件的时候,可以重写一下。比如加入 “点两下back键退出” 功能。

    GestureDetector用来监听和相应对应的手势事件,比如点击,长按,慢滑动,快滑动,用起来很简单,比你自己实现要方便许多。

    DrawFilter可以让你在不调用onDrew方法的情况下,操作canvas,比了个如,你可以在创建自定义 View 的时候设置一个 DrawFilter,给父 View 里面的所有 View 设置反别名。

    ActivityManager.getMemoryClass()告诉你你的机器还有多少内存,在计算缓存大小的时候会比较有用。

    ViewStub它是一个初始化不做任何事情的 View,但是之后可以载入一个布局文件。在慢加载 View 中很适合做占位符。唯一的缺点就是不支持标签,所以如果你不太小心的话,可能会在视图结构中加入不需要的嵌套。

    SystemClock.sleep()这个方法在保证一定时间的 sleep 时很方便,通常我用来进行 debug 和模拟网络延时。

    DisplayMetrics.density这个方法你可以获取设备像素密度,大部分时候最好让系统来自动进行缩放资源之类的操作,但是有时候控制的效果会更好一些.(尤其是在自定义View的时候)。

    Pair.create()方便构建类和构造器的方法。

    Part 3:

    UrlQuerySanitizer——使用这个工具可以方便对 URL 进行检查。

    Fragment.setArguments——因为在构建 Fragment 的时候不能加参数,所以这是个很好的东西,可以在创建 Fragment 之前设置参数(即使在 configuration 改变的时候仍然会导致销毁/重建)。

    DialogFragment.setShowsDialog ()—— 这是一个很巧妙的方式,DialogFragment 可以作为正常的 Fragment 显示!这里可以让 Fragment 承担双重任务。我通常在创建 Fragment 的时候把 onCreateView ()和 onCreateDialog ()都加上,就可以创建一个具有双重目的的 Fragment。

    FragmentManager.enableDebugLogging ()——在需要观察 Fragment 状态的时候会有帮助。

    LocalBroadcastManager——这个会比全局的 broadcast 更加安全,简单,快速。像otto这样的 Event buses 机制对你的应用场景更加有用。

    PhoneNumberUtils.formatNumber ()——顾名思义,这是对数字进行格式化操作的时候用的。

    Region.op()——我发现在对比两个渲染之前的区域的时候很实用,如果你有两条路径,那么怎么知道它们是不是会重叠呢?使用这个方法就可以做到。

    Application.registerActivityLifecycleCallbacks——虽然缺少官方文档解释,不过我想它就是注册 Activity 的生命周期的一些回调方法(顾名思义),就是一个方便的工具。

    versionNameSuffix——这个 gradle 设置可以让你在基于不同构建类型的 manifest 中修改版本名这个属性,例如,如果需要在在 debug 版本中以”-SNAPSHOT”结尾,那么就可以轻松的看出当前是 debug 版还是 release 版。

    CursorJoiner——如果你是只使用一个数据库的话,使用 SQL 中的 join 就可以了,但是如果收到的数据是来自两个独立的 ContentProvider,那么 CursorJoiner 就很实用了。

    Genymotion——一个非常快的 Android 模拟器,本人一直在用。

    -nodpi——在没有特别定义的情况下,很多修饰符(-mdpi,-hdpi,-xdpi等等)都会默认自动缩放 assets/dimensions,有时候我们需要保持显示一致,这种情况下就可以使用 -nodpi。

    BroadcastRecevier.setDebugUnregister ()——又一个方便的调试工具。

    Activity.recreate ()——强制让 Activity 重建。

    PackageManager.checkSignatures ()——如果同时安装了两个 app 的话,可以用这个方法检查。如果不进行签名检查的话,其他人可以轻易通过使用一样的包名来模仿你的 app。

    Part 4:

    Activity.isChangingConfigurations ()——如果在 Activity 中 configuration 会经常改变的话,使用这个方法就可以不用手动做保存状态的工作了。

    SearchRecentSuggestionsProvider——可以创建最近提示效果的 provider,是一个简单快速的方法。

    ViewTreeObserver——这是一个很棒的工具。可以进入到 VIew 里面,并监控 View 结构的各种状态,通常我都用来做 View 的测量操作(自定义视图中经常用到)。

    org.gradle.daemon=true——这句话可以帮助减少 Gradle 构建的时间,仅在命令行编译的时候用到,因为 Android Studio 已经这样使用了。

    DatabaseUtils——一个包含各种数据库操作的使用工具。

    android:weightSum (LinearLayout)——如果想使用 layout weights,但是却不想填充整个 LinearLayout 的话,就可以用 weightSum 来定义总的 weight 大小。

    android:duplicateParentState (View)——此方法可以使得子 View 可以复制父 View 的状态。比如如果一个 ViewGroup 是可点击的,那么可以用这个方法在它被点击的时候让它的子 View 都改变状态。

    android:clipChildren (ViewGroup)——如果此属性设置为不可用,那么 ViewGroup 的子 View 在绘制的时候会超出它的范围,在做动画的时候需要用到。

    android:fillViewport (ScrollView)——在这片文章中有详细介绍文章链接,可以解决在 ScrollView 中当内容不足的时候填不满屏幕的问题。

    android:tileMode (BitmapDrawable)——可以指定图片使用重复填充的模式。

    android:enterFadeDuration/android:exitFadeDuration (Drawables)——此属性在 Drawable 具有多种状态的时候,可以定义它展示前的淡入淡出效果。

    android:scaleType (ImageView)——定义在 ImageView 中怎么缩放/剪裁图片,一般用的比较多的是“centerCrop”和“centerInside”。

    Merge——此标签可以在另一个布局文件中包含别的布局文件,而不用再新建一个 ViewGroup,对于自定义 ViewGroup 的时候也需要用到;可以通过载入一个带有标签的布局文件来自动定义它的子部件。

    AtomicFile——通过使用备份文件进行文件的原子化操作。这个知识点之前我也写过,不过最好还是有出一个官方的版本比较好。

    Part 5:

    ViewDragHelper——视图拖动是一个比较复杂的问题。这个类可以帮助解决不少问题。如果你需要一个例子,DrawerLayout就是利用它实现扫滑。Flavient Laurent 还写了一些关于这方面的优秀文章。

    PopupWindow——Android到处都在使用PopupWindow ,甚至你都没有意识到(标题导航条ActionBar,自动补全AutoComplete,编辑框错误提醒Edittext Errors)。这个类是创建浮层内容的主要方法。

    Actionbar.getThemrContext()——导航栏的主题化是很复杂的(不同于Activity其他部分的主题化)。你可以得到一个上下文(Context),用这个上下文创建的自定义组件可以得到正确的主题。

    ThumbnailUtils——帮助创建缩略图。通常我都是用现有的图片加载库(比如,Picasso 或者 Volley),不过这个ThumbnaiUtils可以创建视频缩略图。译者注:该API从V8才开始支持。

    Context.getExternalFilesDir()———— 申请了SD卡写权限后,你可以在SD的任何地方写数据,把你的数据写在设计好的合适位置会更加有礼貌。这样数据可以及时被清理,也会有更好的用户体验。此外,Android 4.0 Kitkat中在这个文件夹下写数据是不需要权限的,每个用户有自己的独立的数据存储路径。译者注:该API从V8才开始支持。

    SparseArray——Map的高效优化版本。推荐了解姐妹类SparseBooleanArray、SparseIntArray和SparseLongArray。

    PackageManager.setComponentEnabledSetting()——可以用来启动或者禁用程序清单中的组件。对于关闭不需要的功能组件是非常赞的,比如关掉一个当前不用的广播接收器。

    SQLiteDatabase.yieldIfContendedSafely()——让你暂时停止一个数据库事务, 这样你可以就不会占用太多的系统资源。

    Environment.getExternalStoragePublicDirectory()——还是那句话,用户期望在SD卡上得到统一的用户体验。用这个方法可以获得在用户设备上放置指定类型文件(音乐、图片等)的正确目录。

    View.generateViewId()——每次我都想要推荐动态生成控件的ID。需要注意的是,不要和已经存在的控件ID或者其他已经生成的控件ID重复。

    ActivityManager.clearApplicationUserData()—— 一键清理你的app产生的用户数据,可能是做用户退出登录功能,有史以来最简单的方式了。

    Context.createConfigurationContext()——自定义你的配置环境信息。我通常会遇到这样的问题:强制让一部分显示在某个特定的环境下(倒不是我一直这样瞎整,说来话长,你很难理解)。用这个实现起来可以稍微简单一点。

    ActivityOptions——方便的定义两个Activity切换的动画。 使用ActivityOptionsCompat可以很好解决旧版本的兼容问题。

    AdapterViewFlipper.fyiWillBeAdvancedByHostKThx()——仅仅因为很好玩,没有其他原因。在整个安卓开源项目中(AOSP the Android ——pen Source Project Android开放源代码项目)中还有其他很有意思的东西(比如

    GRAVITY_DEATH_STAR_I)。不过,都不像这个这样,这个确实有用

    ViewParent.requestDisallowInterceptTouchEvent()——Android系统触摸事件机制大多时候能够默认处理,不过有时候你需要使用这个方法来剥夺父级控件的控制权。

    1、android:clipChildren 和 android:clipToPadding:clipToPadding就是说控件的绘制区域是否在padding里面的,true的情况下如果你设置了padding那么绘制的区域就往里 缩,clipChildren是指子控件是否超过padding区域,这两个属性默认是true的,所以在设置了padding情况下,默认滚动是在 padding内部的,要达到上面的效果主要把这两个属性设置了false那么这样子控件就能画到padding的区域了。使用场景如:ActionBar(透明)下显示Listview而第一项要在actionbar下。参见android:clipToPadding和android:clipChildren。

    2、Fragment 的 setUserVisibleHint 方法,可实现 fragment 对用户可见时才加载资源(延迟加载)。

    3、自定义 View 时重写 hasOverlappingRendering 方法指定 View 是否有 Overlapping 的情况,提高渲染性能。

    4、AutoScrollHelper,在可滚动视图中长按边缘实现滚动,Android View.OnTouchListener 的子类。

    5、TouchSlop,系统所能识别出的被认为是最小的滑动距。离,ViewConfiguration.get(context).getScaledTouchSlop()。

    6、VelocityTracker,可用于 View 滑动事件速度跟踪。

    7、AlphabetIndexer,字母索引辅助类。

    8、Messenger,AIDL 实现的封装,比手写 AIDL 更方便。

    9、ArrayMap,比 HashMap 更高的内存效率,但比 HashMap 慢,不适合有大量数据的场景。

    10、Property,抽象类,封装出对象中的一个易变的属性值,使用场景如在使用属性动画时对动画属性的操作。

    11、SortedList,v7 包中,见名知意。

    HandlerThread 单一线程 + 任务队列 处理轻量的异步任务 详见详解 Android 中的 HandlerThread

    Proguard assumenosideeffects 编译器屏蔽日志 详见关于Android Log的一些思考

    Android性能调优利器StrictMode

    Android中线程优先级控制 android.os.Process.setThreadPriority 详见剖析Android中进程与线程调度之nice

    Android Lint 一个静态分析工具 详见使用Android lint发现并解决高版本API问题

    (更新) 使用UncaughtExceptionHandler 处理应用崩溃,收集信息甚至可以不显示崩溃对话框 详细:Android处理崩溃的一些实践

    1、getParent().requestDisallowInterceptTouchEvent(true);剥夺父view对touch事件的处理权,谁用谁知道。

    2、ArgbEvaluator.evaluate(float fraction,ObjectstartValue,ObjectendValue);用于根据一个起始颜色值和一个结束颜色值以及一个偏移量生成一个新的颜色,分分钟实现类似于微信底部栏滑动颜色渐变。

    3、Canvas中clipRect、clipPath和clipRegion剪切区域的API。

    4、Bitmap.extractAlpha ();返回一个新的Bitmap,capture原始图片的alpha值。有的时候我们需要动态的修改一个元素的背景图片又不希望使用多张图片的时候,通过这个方法,结合Canvas和Paint可以动态的修改一个纯色Bitmap的颜色。

    5、HandlerThread,代替不停new Thread开子线程的重复体力写法。

    6、IntentService,一个可以干完活后自己去死且不需要我们去管理子线程的Service。

    7、Palette,5.0加入的可以提取一个Bitmap中突出颜色的类,结合上面的Bitmap.extractAlpha,你懂的。

    8、Executors.newSingleThreadExecutor();这个是java的,之前不知道它,自己花很大功夫去研究了单线程顺序执行的任务队列。。

    9、android:animateLayoutChanges=”true”,LinearLayout中添加View的动画的办法,支持通过setLayoutTransition()自定义动画。

    10、ViewDragHelper,自定义ViewGroup处理各种事件很累吧,嗯? what the fuck!!

    11、GradientDrawable,之前接手公司的项目,发现有个阴影效果还不错,以为是切的图片,一看代码,什么鬼= =!

    12、AsyncQueryHandler,如果做系统工具类的开发,比如联系人短信辅助工具等,肯定免不了和ContentProvider打交道,如果数据量不是很大的情况下,随便搞,如果数据量大的情况下,了解下这个类是很有必要的,需要注意的是,这玩意儿吃异常..

    13、ViewFlipper,实现多个view的切换(循环),可自定义动画效果,且可针对单个切换指定动画。

    14、有朋友提到了在自定义View时有些方法在开启硬件加速的时候没有效果的问题,在API16之后确实有很多方法不支持硬件加速,通常我们关闭硬件加速都是在清单文件中通过,其实android也提供了针对特定View关闭硬件加速的方法,调用View.setLayerType(View.LAYER_TYPE_SOFTWARE, null);即可。

    15、android util包中的Pair类,可以方便的用来存储一”组”数据。注意不是key value。

    16、PointF,graphics包中的一个类,我们经常见到在处理Touch事件的时候分别定义一个downX,一个downY用来存储一个坐标,如果坐标少还好,如果要记录的坐标过多那代码就不好看了。用PointF(float x, float y);来描述一个坐标点会清楚很多。

    17、StateListDrawable,定义Selector通常的办法都是xml文件,但是有的时候我们的图片资源可能是从服务器动态获取的,比如很多app所谓的皮肤,这种时候就只能通StateListDrawable

    来完成了,各种addState即可。

    18、android:descendantFocusability,ListView的item中CheckBox等元素抢焦点导致item点击事件无法响应时,除了给对应的元素设置 focusable,更简单的是在item根布局加上android:descendantFocusability=”blocksDescendants”

    19、android:duplicateParentState=”true”,让子View跟随其Parent的状态,如pressed等。常见的使用场景是某些时候一个按钮很小,我们想要扩大其点击区域的时候通常会再给其包裹一层布局,将点击事件写到Parent上,这时候如果希望被包裹按钮的点击效果对应的Selector继续生效的话,这时候duplicateParentState就派上用场了。

    20、includeFontPadding=”false”,TextView默认上下是有一定的padding的,有时候我们可能不需要上下这部分留白,加上它即可。

    21、Messenger,面试的时候通常都会被问到进程间通信,一般情况下大家都是开始背书,AIDL巴拉巴拉。。有一天在鸿神的博客看到这个,嗯,如他所说,又可以装一下了。

    22、TextView.setError();用于验证用户输入。

    23、ViewConfiguration.getScaledTouchSlop();触发移动事件的最小距离,自定义View处理touch事件的时候,有的时候需要判断用户是否真的存在movie,系统提供了这样的方法。

    24、ValueAnimator.reverse(); 顺畅的取消动画效果。

    25、ViewStub,有的时候一块区域需要根据情况显示不同的布局,通常我们都会通过setVisibility的方法来显示和隐藏不同的布局,但是这样默认是全部加载的,用ViewStub可以更好的提升性能。

    26、onTrimMemory,在Activity中重写此方法,会在内存紧张的时候回调(支持多个级别),便于我们主动的进行资源释放,避免OOM。

    27、EditTxt.setImeOptions, 使用EditText弹出软键盘时,修改回车键的显示内容(一直很讨厌用回车键来交互,所以之前一直不知道这玩意儿)

    28、TextView.setCompoundDrawablePadding,代码设置TextView的drawable padding。

    29、ImageSwitcher,可以用来做图片切换的一个类,类似于幻灯片。

    30、java8中新增的LocalDate和LocalTime接口,Date虽然是个万能接口,但是它真的不好用,有了这俩,终于可以愉快的处理日期时间了。

    31、WeakHashMap,直接使用HashMap有时候会带来内存溢出的风险,使用WaekHashMap实例化Map。当使用者不再有对象引用的时候,WeakHashMap将自动被移除对应Key值的对象。

    清除画布上的内容:canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);

    在自定义View的onDetachedFromWindow方法中清理与View相关的资源;

    Fragment在onAttach方法中接收回调:

    @Override

    public void onAttach(Activityactivity) {

    super.onAttach(activity);

    try{

    mPageSelectedListener = (PageSelectedListener) activity;

    mMenuBtnOnclickListener = (MenuBtnOnClickListener) activity;

    mCommitBtnOnClickListener = (CommitBtnOnClickListener) activity;

    }catch(ClassCastExceptione) {

    thrownewClassCastException(activity.toString() +”must implements listener”);

    }

    }

    使用ClipDrawable实现进度条功能;

    自定义view中的getContext(),再也不需要专门创建一个mContext全局对象了;

    自定义手写view的时候,在手指移动的过程中通过MotionEvent | Android Developers对象的getHistorySize()获得缓存的历史点,绘制出来的曲线要平滑很多。

    复写Activity的onUserLeaveHint方法,确保用户离开界面时能够立即暂停界面中的一些任务,关于onUserLeaveHint的更多作用可以谷歌:android - Google 搜索

    值得借鉴的点击两次退出应用的实现:Android关于双击退出应用的问题

    Lollipop Dialer 接通画面里面那些酷炫的动画其实只有一行:android:animateLayoutChanges=”true” 然后 setVisibility 就可以了

    LocalBroadcastManager 可以在App的范围内发广播和收广播,不会被global的receiver收到,对数据比较敏感且不用共享的可以用这个。

    1. 通过 WindowManager.addView 在其他app界面添加一个view时,经常会无法显示,特别在miui,emui固件上,需要指定type为LayoutParams.TYPE_TOAST。

    2. View.getLocationOnScreen(new int[]),获取view在屏幕上的位置

    3. Paint.setXfermode(porterDuffXfermode),在ApiDemo里面有专门的介绍,实现了穿透,叠加,覆盖等多种绘制效果,非常实用

    4. IBinder binder = ServiceManager.getService(“wallpaper”);

    IWallpaperManager wm = IWallpaperManager.Stub.asInterface(binder);

    Bundle params = new Bundle();

    ParcelFileDescriptor fd = wm.getWallpaper(stub, params);

    直接获取当前系统壁纸的fd,避免壁纸过大造成oom问题。这种方式有适配问题,需注意。

    1. 通过View.getDrawingCache()可以获取截图,但是需要setDrawingCacheEnabled(true)频繁使用可能会oom,还有一种方法直接用canvas

    Bitmap bm = Bitmap.createBitmap((int) (w * scale), (int) (h*scale), Bitmap.Config.ARGB_8888);

    Canvas canvas = new Canvas();

    canvas.setBitmap(bm);

    View.draw(canvas);

    return bm;

    1. 说到几个oom,顺带说下有一种偷懒又有效的解决办法,在manifest上加android:largeHeap=”true”

    2. 用一个牛逼的来结尾,AccessibilityService。由于强大所以需要手动设置来启用,各种消息获取各种状态拦截,豌豆荚就用ta实现了非root自动安装。

    View中的isShown()方法,以前都是用view.getVisibility() == View.VISIBLE来判断的

    这个方法是个坑,并不是像描述的那样,如果view是个 ViewGroup,如果某个子view是不可见的,其他可见,这个方法照样会返回fasle

    Fragment.instantiate(Contextcontext,Stringfname,Bundleargs);

    Arrays.toString();

    AtomicInteger,一个提供原子操作的Integer的类。在Java语言中,++i和i++操作并不是线程安全的,在使用的时候,不可避免的会用到synchronized关键字。而AtomicInteger则通过一种线程安全的加减操作接口。这个类在AsyncTask中用到了。

    原文链接:http://www.jianshu.com/p/a573399eb182

    展开全文
  • 脑机接口(BCI)的研究方法

    千次阅读 多人点赞 2020-10-19 14:52:30
    迁移学习 迁移学习消除了重新收集数据和重建模型的需要,它通过交换在一个相对相似的任务上的分类器学习到的信息来提高分类器的性能,迁移学习的成功取决于任务的选择,两个任务没有太多确定的相关性,迁移学习可能...

    “ 嘘!悄悄插播的哦”
    在这里插入图片描述
    (图1 BCIduino实物图)
    BCIduino 8通道脑电放大器具体参数如下:
    输入阻抗:1TΩ
    输入偏置电流:300pA
    输入参考噪声:1μVpp
    采样速率:250 Hz/500Hz
    共模抑制比:-110dB
    可调增益放大倍数:1、2、4、6、8、12、2
    分辨率:24 位 ADC,精度最高可达 0.1μV
    功耗:正常工作时 39mW,待机时低至仅 10μW
    采用可充电锂电池供电,进一步降低来自外部的干扰。
    尺寸:50mm*50mm(实物测量,存在细微误差),实物图如图1
    在这里插入图片描述
    图2 BCIduino在普通嘈杂环境下、悬空状态的数据波形,可以观测到并无其他干扰出现
    在这里插入图片描述
    图3 OpenBCI在普通嘈杂环境下、悬空状态的数据波形(测量环境、测量时间、软件滤波器设置参数与图2BCIduino相同)

    近年来,**脑机接口(Brain-Computer Interface)**的进步不仅有助于实现其帮助残疾人的根本目的,而且也有助于创造新的应用程序。脑电活动由脑电装置测量,采集的原始数据经过信号采集、数据预处理、特征提取和分类等步骤。本文将对这些过程中使用的不同算法和方法进行详细介绍。

    我们与外部环境的互动是一种日常现象。无论是口头交流还是身体控制,所有形式的互动最终都取决于肌肉和控制它们的神经。每一个动作,都是大脑某些区域启动的复杂过程的结果,神经系统将大脑产生的信号传递给相应的肌肉群来执行特定的动作。脑机接口(BCI)增强了这种自然的交流和控制方式。

    脑机接口系统捕捉大脑活动,并将其转化为应用程序可以理解的信息或命令,以执行特定任务。发展脑机接口的主要动机是为残疾人提供交流手段。因此有一种误解,认为脑机接口的范围仅限于医学领域。实际上脑机接口的适用性已经远远超出了医学领域。尽管如此,脑机接口系统仍然存在许多有待解决的实际挑战。

    BCI系统的组成
    BCI系统由信号采集信号处理应用接口反馈机制四部分组成。
    信号采集模块使用两种技术(即有创或无创电极)记录大脑活动。
    信号处理模块帮助从记录的信号中提取必要的信息,它由数据预处理、特征提取和分类算法组成。
    转换后的信号通过应用程序接口进行通信。
    最后,反馈机制实时产生结果,帮助用户了解系统所采取的行动。

    什么是脑波?
    脑波是许多神经元在某些认知活动中相互交流的同步电脉冲的结果。它们分为四组:α、β、θ和δ。
    每当大脑忙于某些心理活动时,就会产生β波。这些是最快的波,振幅较低,频率在15到40赫兹之间。
    当大脑处于非唤醒状态时,会观察到α波。完成一项任务并坐下来休息的人通常处于阿尔法状态。它们具有较高的振幅,频率约为9至14赫兹。
    θ波的振幅更大,频率更慢,范围为5-8赫兹。三角波是所有波中速度最慢、振幅最大的波。频率范围从1赫兹到4.5赫兹。δ波不能达到零,因为它象征着一个死亡的大脑。
    在这里插入图片描述
    操作步骤详解
    信号采集这一步骤是捕捉脑机接口系统的大脑活动,而用于记录大脑信号的技术分为非侵入性、半侵入性以及侵入性。
    对于侵入式技术,将微型电极直接放置在皮层,可以测量单个神经元的活动。在半侵入技术中,电极被放置在大脑暴露的表面上。
    在非侵入性的方式,传感器被放置在头皮上,以测量大脑产生的电位,脑电图(EEG)是一种广泛应用的无创技术。
    脑电图(Electroencephalogram)是一种非侵入性技术,它测量大脑中的电脉冲。该系统将从安装在病人头部的脑电图帽或脑电图耳机接收信号,放在头皮上的电极用来测量电信号的振幅。一般来说,正常脑电图信号的范围是每秒1到100个周期,但是当振幅在10到100微伏之间时,每秒100个周期是非常罕见的。

    常用预处理技术
    通常情况下,信号的空间分辨率很低,任何响应的信噪比也很低。在信号的记录过程中,各种观察和结论都与信息信号相结合,眨眼、肌肉活动、背景中的活动是信号预处理前需要处理的噪声。
    数据预处理阶段紧随数据采集阶段。数据采集阶段采集到的数据不干净,有噪声,掺杂了噪声和人工制品,如眨眼、眼睛运动、心跳、肌肉运动等。因此,预处理是必不可少的。
    在预处理阶段,得到清晰的数据,进一步进行特征提取和分类,以下是常用的几种预处理方式:
    独立成分分析(Independent Component Analysis)
    在独立分量分析中,考虑到伪影和脑电信号是相互独立的,从而将它们分开。该算法将脑电数据分解为空间固定和时间无关的分量,当需要分解大量的数据时,它的性能很好。一些研究表明,ICA在许多应用中都优于经典的主成分分析Principle Component Analysis,而且ICA特别擅长于从EEG中去除眼部伪影,特别是当眼信号和脑信号的幅度相当时。
    使用共同平均参考值(Common Average Reference)
    脑电信号中的噪声可能是常见的活动,伪影的存在导致脑电信号的信噪比较低,在这种算法中,计算了所有电极的平均值,通过去除所有电极的平均值来消除噪声。实验表明,CAR优于所有的参考方法,显示出最好的分类精度结果。参考法计算平均值问题的主要原因是脑电电极阵列的头部覆盖不完整和样本密度有限。
    在这里插入图片描述
    特征提取与选择
    一旦数据被清理干净,我们就从信号中提取必要的信息,特征提取和选择算法帮助我们为应用程序选择最突出的特征。
    首先,我们提取epoch:一个特定长度的与事件(即刺激)同步的EEG信号块,然后计算这些时期的统计数据,提取特征。
    特征提取
    最常用的特性是时间点和频带功率。频带功率主要用于运动想象、解码心理状态或基于稳态视觉诱发电位(SSVEP)的脑机接口。它们代表给定频带在给定时间窗内的平均EEG信号值。
    时间点特征主要用于ERP的分类,例如基于P300的BCI,时间点特征是从所有通道采集的脑电图样本。许多BCI研究表明,将不同类型的特征组合起来,即频带功率时间点频带功率连通性特征,通常会产生更好的结果,比仅使用一种特征类型的分类精度更高。
    特征选择
    一旦特征被提取出来,我们就可以应用选择方法来选择唯一能提供潜在优势的特征子集。以下原因证明了此步骤的必要性:
    ①从所提取的各种特征中,有些特征可能是重复的,或者没有任何方式与脑机接口关注的心理状态相一致。
    ②减少特征集合,充分减少分类器需要调整的参数,从而减少过拟合的几率,提高执行效率。
    ③从信息抽取的角度来看,如果只选择几个特征,就更容易看出哪些特征真正与关注的心理状态一致。
    ④特征越小,预测越快。
    ⑤最后,数据的大小将减小。
    最常用的方法:包装器、过滤器和嵌入式方法。
    过滤方法独立于所使用的分类器,它使用每个特征和目标类之间的关系度量,为了根据重要性对特征进行排序,可以使用确定系数。但是这种技术只捕获数据和类之间的线性依赖关系,可能会输出重复特征的选择,嵌入式和包装器方法可以为上述问题提供解决方案。

    分 类
    复杂的模式识别和分类算法需要将神经信息转化为控制信号,脑机接口研究团体广泛致力于开发能够对神经活动进行分类的算法。基于脑电的分类算法主要分为四类:自适应分类器、矩阵和张量分类器、转移学习和深度学习。
    自适应分类器
    在获得大量新的EEG数据后,自适应分类器的线性判别超平面中的每个特征的权重被稳定地重新估计和更新。因此,当EEG数据的非平稳变化被添加到一个新的特征中时,EEG的分布就变得更加精确。这有助于我们在每一个新的数据生成周期之后对分类进行微调。
    矩阵和张量分类器
    张量分类器通常使用地形图、TFR、时空频率和多线性PCA作为输入特征,张量化和低秩张量分解可以有效地将高维数据压缩成低阶因子矩阵和/或核张量,这些矩阵通常表示简化的特征。张量法还可以将以大规模矩阵表示的耦合试验块分析为张量形式,以便将原始脑电数据中的相关成分从独立成分中分离出来。
    迁移学习
    迁移学习消除了重新收集数据和重建模型的需要,它通过交换在一个相对相似的任务上的分类器学习到的信息来提高分类器的性能,迁移学习的成功取决于任务的选择,两个任务没有太多确定的相关性,迁移学习可能会失败。脑机接口中的迁移学习需要定义离散条件,只有在这些条件下才能应用。
    深度学习
    下面的深度学习方法可以将运动图像显著地分类为左手、右手、手腕等动作。
    ①卷积神经网络(Convolutional Neural Network):
    在卷积神经网络(CNN)中,基于输入矩阵,在每个卷积层后生成一定数量的激活映射。然后使用最大值或平均值来标识最常见或最主要的特性。最后,激活映射通过过滤层,从而被展平为一维向量。
    ②RBMS:
    Boltzmann机器(Restricted Boltzmann Machines,RBMS)是一种生成性神经网络,它在给定一组输入数据点的情况下,形成一个二部图(即可见层和隐层神经元相互连接,但同一层神经元之间没有连接)来学习概率分布。经过适当的变换,RBMS可用于对运动图像的基于频率的EEG表示进行分类。
    在这里插入图片描述
    BCI应用
    脑机接口并不局限于医学领域,BCI在其他领域正在以创造性的方式发展。
    ①医疗
    使用BCI,我们可以测量一个人的精神状态,检测甚至预测健康问题,如肿瘤、睡眠障碍等。使用BCI的假肢可以在一定程度上恢复活动能力,从而使他们能够做一些家务。
    ②家庭自动化
    BCI可用于家庭自动化,我们可以利用大脑产生的信号控制家里的各种电器,这完全改变了我们与环境互动的方式。
    ③营销、广告和娱乐
    一些研究者利用脑电图研究了不同广告的记忆程度,从而提供了一种评价广告的方法,可以设计出各种各样的游戏,用大脑与用户互动。
    ④教育
    神经反馈,利用大脑的电信号来确定所研究信息的清晰程度。
    ⑥安全性
    像认知生物识别或电生理学这样的安全系统可以被开发出来,它使用用户的大脑信号进行身份验证,这比密码、生物特征识别或其他方法更安全。认知生物特征学或电生理学更安全,因为它们使用的是外部观察者无法捕捉或获取的大脑信号。
    在这里插入图片描述
    结语
    本文就脑机接口研究作了简要的概述,我们了解到BCI系统基本上由四个主要部分组成:信号采集、信号处理、应用接口和反馈。同时我们也了解到脑机接口的应用不仅限于作为严重残疾人士的交流工具。教育、安全、自动化和娱乐等其他领域也在利用BCI技术进行创造性的发展,同时BCI的发展仍然伴随着一些有待解决的问题。
    最后,我们希望通过这篇文章能让读者简单了解脑机接口(BCI)以及它未来发展的巨大潜力。

    客官请留步
    在这里插入图片描述
    在这里插入图片描述
    参考文献
    [1] T.P. Jung, S. Makeig and C. Humphries, “Extended ICA Removes
    Artefacts from Electroencephalographic Recording”, Advances in Neural
    Inf. Processing Systems, Cambridge, Vol. 10, 1998.
    [2] M. Rajya Lakshmi, Dr. T. V. Prasad and Dr. V. Chandra Prakash, “Survey
    on EEG Signal Processing Methods”, International Journal of Advanced
    Research in Computer Science and Software Engineering, 2014.
    [3] Mourino, J & Millan, Jose del R. & Cincotti, Febo & Chiappa, S & Jané,
    Raimon & Babiloni, Fabio, “Spatial filtering in the training process of
    a Brain-Computer interface”, 2001
    [4] Siyi Deng, William Winter, Samuel Thorpe, Ramesh Srinivasan, “EEG
    Surface Laplacian using realistic head geometry”, Int. J. of Bioelectro-
    magnetism, Vol. 13, No. 4, pp. 173-177, 2011.
    [5] C. Tandonnet, B. Burle, T. Hasbroucq, F. Vidal, “Spatial Enhancement
    of EEG traces by Surface Laplacian Estimation: Comparison between
    Local and Global Methods”, Clinical Neurophysiology 116 (2005)
    18–24.
    [6] Prashant Lahane and Thirugnanam Mythili, “ Literature review of
    Human Stress analysis using EEG Signal”, Research Journal of Phar-
    maceutical, Biological and Chemical Sciences, 2016.
    [7] Event Related Potentials, Neurotechedu [online],
    http://learn.neurotechedu.com/erp/ [Accessed: October 3, 2018]
    [8] F Lotte et al, “A review of classification algorithms for EEG based
    brain–computer interfaces: a 10 year update “, J. Neural Eng. 2018.
    [9] Cichocki A, “Tensor decompositions: a new concept in brain data
    analysis?” J. Soc. Instrum. Control Eng., 2011
    [10] Grosse-Wentrup M, “What are the causes of performance variation in
    brain–computer interfacing” Int. J. Bioelectromagn, 2011

    本篇由BCIduino脑机接口开源社区整理或撰写。BCIduino脑机接口社区由来自北京航空航天大学、康奈尔大学、北京大学、首都医科大学等硕博发起成立,欢迎扫码加入社群,备注"BCI",也欢迎采购BCIduino脑电模块(某宝搜索即可)

    展开全文
  • LCD各接口类型与定义

    千次阅读 2014-12-23 17:22:30
    HDMI比DFP的改进是加入了版权保护HDCP的同时增加了数字音频传输,从而成为专用的多媒体信息接口。HDMI支持1920*1080P高清晰数字信号,并支持Dolby Digital/DTS数字音频格式。HDMI可望成为未来的视频设备的标准接口。...
  • 各种显示接口DBI、DPI、LTDC、DSI、FSMC

    千次阅读 多人点赞 2019-06-05 17:30:36
    DBI接口最大的特点就是LCM自带framebuffer, 存储Host端发过来的数据, 并由内部的控制IC不断重复的刷到LCD上, 也就是说其内部实现了自刷新, Host只要发送次数据即可, 帧数据会一直显示在屏幕上。由于不...
  • ,在控上选择一个2档开关和一个3档开关,进行关联设置,组合得到6个不同档位,使得设置好后: 当2档开关处于第1档位时:3档开关的1/2/3档,分别对应模式1/3/5 当2档开关处于第2档位时:3档开关的1/2/3档...
  • 虽然很多时候一个api接口的业务,数据逻辑是后端提供的,但真正使用接口的是客户端,一个前端功能的实现流程与逻辑,有时候只有客户端的RD才清楚,从某种意义来说,客户端算是接口的需求方。所以建议在前期接口...
  • USB接口和雷电接口有什么关系?

    千次阅读 2019-11-30 22:00:00
    关注、星标公众号,不错过精彩内容作者:strongerHuang公众号:strongerHuang随着USB 3.2和Tpye-C的普及,可能在一些地方会说雷电接口,但是很多人还是模糊不...
  • 使用Spring Cloud做项目的同学会使用Feign这个组件进行远程服务的调用,Feign这个组件采用模板的方式,有着优雅的代码书写规范。核心原理对Feign等相关注解进行解析,并提取信息,在Spring Boot工程启动时...
  • Java后端开发工作 - 写接口

    千次阅读 2020-04-15 16:52:22
    一个接口可以理解为一个业务逻辑,一个业务逻辑可以由1~n个SQL组成。一个优质的接口,应该是通用的接口,一旦需求变了,给过来的参数有变化,那我尽量做到接口不变,你多给我一个参数或者某个参数变化一下,我就...
  • 关于POS接口配置的几注意事项

    万次阅读 2016-01-27 10:53:30
    POS接口配置的几注意事项 POS接口配置的几注意事项  POS接口在与对端路由器,特别是CISCO路由器对接的过程中,需要注意以下一些事项,否则可能导致我司路由器不能与对方互通。    1.我们的NE路由器...
  • 接口设计---更好的提高效率

    千次阅读 2018-08-02 17:38:12
    以下是一个用户信息接口的文档示例,包含接口描述,请求参数,响应参数,json示例等。 接口描述:用户登陆成功后,或进入个人中心时会获取一次用户信息 URI 方法 /userinfo GET 请求参数 名称 必填 备注 id 是...
  • C#接口编程接口

    千次阅读 2006-04-20 00:42:00
    C#接口编程接口()www.xyhhxx.com 发布者: seo 时间: 2005-08-11 作者:黎宇 接口(interface)用来定义一种程序的协定。实现接口的类或者结构要与接口定义严格一致
  • 1、其中其使用input_shape=(12,),这个指的是输入维的数据,其例子如下: # as first layer in a Sequential model model = Sequential() model.add(Reshape((3, 4), input_shape=(12,))) # now: model.output_...
  • 电脑常见的接口大全

    万次阅读 2013-11-09 21:00:38
    每一台计算机,不管是台式机还是笔记本,里里外外都有很多的接口,你能把它们每一个都认出来而且知道它们的用途吗?通常一些相关的文章介绍起来都缺乏耐心,而且也没有足够的插图之类,更使得大家犯迷糊。  本文...
  • c#的接口

    千次阅读 2008-06-15 11:44:00
    接口慨述 接口(interface)用来定义一种程序的协定。实现接口的类或者结构要与接口定义严格一致。有了这个协定,就可以抛开编程语言的限制(理论上)。接口可以从多接口继承,而类或结构可以实现多...
  • Java Web系统常用的第三方接口

    万次阅读 2010-12-27 10:35:00
    1、Web Service接口 1.1 接口方式说明和优点 1.2 重要概念 1.3 开发Web Service接口和调用测试 1.4 开发实例 2、js接口 2.1 接口方式说明和优缺点 2.2 开发实例 3、http接口 3.1 接口方式说明和优...
  • Java Web项目常用的第三方接口

    千次阅读 2017-11-09 11:36:03
    感谢作者 ...1. Web Service接口 ...1.1 接口方式说明和优点 ...在笔者的开发生涯中,当作为接口提供商给第三方提供接口时,以及作为客户端去调用第三方提供的接口时,大部分时候都是使用Web Service接口,We
  • 一个最常见的API错误设计是使用外来的规则,API属于一个特定的平台和相关开发者生态系统。你不能使用任何其他不同平台的描述用语或构架,会污染你当前的代码库,并破坏你同伴的工作效率。 在编码前要充分了解你...
  • Web Service 接口

    千次阅读 2016-05-31 16:36:14
    1. Web Service 接口 1.1 接口方式说明和优点 在笔者的开发生涯中,当作为接口提供商给第三方提供接口时,以及作为客户端去...Web Service的主要目标是跨平台的可互操作性,为了实现这一目标, Web Service
  • 最近需要为网站加入支付宝的充值接口,而目前关于支付宝接口开发的资料比较杂乱,这里就我此次开发所用到的资料进行汇总整理,希望能够帮助需要的朋友。 开发步骤: 1. 确定签约类型 支付宝的接口有多种类型,...
  • 共69道,2W字,耗时两天(疯狂暗示)欢迎催更吹水,来一个人就是一份催更动力点击并输入暗号:CSDN 目录1. 按你的理解,软件接口是什么?2.HTTP 和 HTTPS 协议区别?3.HTTPS 在哪一层?4.get 和 post 区别是什么?5....
  • 那么比如我想制作一个OCX控件,在控件里加载一个网页这种该如何实现了。熟悉MFC的应该知道,MFC里可以直接使用webbrowser个ActiveX控件,使用webbrowser可以很方便的加载网页。下面讲解该如何实现一功能。 首先...
  • 【Golang】interface接口设计原则

    万次阅读 2020-06-15 12:22:19
    它跟java,C# 不太一样,不需要显示说明实现了某个接口,它没有继承或子类或“implements”关键字,只是通过约定的形式,隐式的实现interface 中的方法即可。因此,Golang 中的 interface 让编码更灵活、易扩展。 ...
  • 一个架构师谈什么是架构以及怎么成为一个架构师

    万次阅读 多人点赞 2015-02-24 20:05:45
    此篇源出自我在公司内部写的一个PPT,它是用于在公司内部向广大技术人员做普及用的一个资料,而CSDN这边的编辑不支持图文混排的效果,因此一些章节我就直接截取自我的PPT里的内容了,这样可能对大家在阅读上会显
  • 支付接口解释

    千次阅读 2016-03-04 18:02:39
    最近需要为网站加入支付宝的充值接口,而目前关于支付宝接口开发的资料比较杂乱,这里就我此次开发所用到的资料进行汇总整理,希望能够帮助需要的朋友。 开发步骤: 1. 确定签约类型 支付宝的接口有多种...
  • Java的接口和抽象类

    千次阅读 2016-04-17 11:21:45
    抽象类与接口是java语言中对抽象概念进行定义的两种机制. ...如果一个类含有抽象方法,则称个类为抽象类,抽象类必须在类前用abstract关键字修饰。但是不能用他实例化对象,因为它含有抽象的方法

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 81,784
精华内容 32,713
关键字:

定义接口这个接口装定一个方法显示信息