热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

在VisualC#中定义和使用自己的特性

复杂的,面向组件的业务开发,期待现代的软件开发工程师们具备更多的弹性设计,而不是过去的方法设计。微软的.NET框架通过众所周知的声明式编程,广泛
复杂的,面向组件的业务开发,期待现代的软件开发工程师们具备更多的弹性设计,而不是过去的方法设计。微软的
.NET框架通过众所周知的声明式编程,广泛的使用特性来附加额外的功能。在软件系统里,特性可以增强系统的弹性,这是因为,特性使功能的松耦合得到了增强。所以,你可以定制自己的特性类,然后根据你自己的意图,合理的使用这些具有松耦合功效的特性。



  使用.NET框架编写Windows程序,在很多方面已经变得很简单。在许多情况下,.NET框架使用,.NET编译器在编译时绑定到程序集的元数据,.使灵活的程序设计变得更容易。事实上,对于.NET而言,使用内嵌的元数据把我们从DLL地狱解脱出来是可能的。



  值得庆幸的是,.NET框架的设计者们并没有选择把这些元数据优雅的隐藏起来。设计者们把反射API给予了我们,通过反射,一个.NET程序可以通过编程查看这个元数据。一个程序可以反射出包含在特定程序集内任意的东西,或者说是包含在其内的所有的类型和成员。



  把元数据绑定到可执行的程序集里,提供了许多优势。这使得.NET程序集,完全可以自我描述。还允许开发者跨语言共享组件,去除了头文件的需要。(这些头文件会由于相关的实现代码而过期。)



  关于.NET元数据所有积极的消息,看起来很难相信,它好像什么也没有,仅仅是个谎言。但是,它确实是存在的。在.NET里,你可以创建自己特定程序的元数据,并且可以把这些元数据应用到你可以想象到的地方。



  开发者通过使用自定义特性,可以定义他们自己特定程序的元数据。因为这些特性的值将变成另一部分元数据,绑定到一个程序集里。所以这些自定义特性的值可以被反射API检查到并且可以被使用。



  我们经常提到一个类的属性,这些属性的值可以作为特性来使用。那么属性和自定义特性真正的区别在哪里呢?



  通过这篇文章,你将学会如何定制特性,如何把特性应用到你的源代码类和方法上,以及如何使用反射API获取和使用这些特性的值。



  
公共语言运行时是如何使用特性的?



  在你开始考虑如何使用你自己定义的特性类之前,让我们查看一些标准的特性,这些已经在公共语言运行时有用到。



  [WebMethod]特性提供了一个简单的例子。它可以使WebService派生的子类中任意公共的方法转化成Web Service暴露方法的一部分,而这一切,仅仅通过把[WebMethod]附加到方法的定义上就可以做到。




public class SomeWebService : System.Web.Services.WebService
{
[WebMethod]
public DataSet GetDailySales()
{
//处理请求的代码
}
}


  你只要把[WebMethod]特性添加到一个方法上,.NET就会在后台为你处理其它所有的事情。



  在给定的方法上使用[Conditional]特性,那么此方法是否可调用将取决于指定的预处理标识符是否被定义。举个例子,看如下的代码:




public class SomeClass
{
[Conditional("DEBUG")]
public void UnitTest()
{
//单元测试代码
}
}


  这段代码说明,该类的方法UnitTest()是否有效,将取决于预处理标识符“DEBUG”是否被定义(译注:在编译调试版本时,DEBUG常数已经被定义)。我们可能更感兴趣的是,使用[Conditional]后真正发生了什么。当条件失效时,编译器将会停止所有对该方法的调用,相比有同样功能的预处理指令#if...#endif,此方法显得更简洁,而且,使用这项功能,我们不需要多做任何事情。



  特性使用了定位参数和命名参数。在使用了[Conditional]特性的例子中,特定的符号就是定位参数。定位参数是强制性的,你必须提供。



  让我们回到使用了[WebMethod]特性的例子,来看一下命名参数。这个特性有一个Description的命名参数,可以像下面这样使用:




[WebMethod(Description = "Sales volume")]


  命名参数是可选择的,参数的值要紧跟着写在参数名字的后面。如果存在定位参数,那么你需要先书写定位参数,然后在定位参数的后面书写命名参数。



  我将会在文章的后面讲述更多关于定位参数和命名参数的内容,这将在我向你展示如何创建和使用你自己的特性类时提到。



  
特性可用于运行时,设计时



  在这篇文章里,我提供的都是与运行时的行为相关的例子。但是二进制文件(程序集)并不只是用于运行时。在.NET里,你所定义的元数据也不只是局限于运行时,相反,当你编译成程序集后,在任何时候你都可以查阅这些元数据。



  考虑在设计时,使用元数据的一些可能的情况。在Visual Studio.Net里,使用IDE可以构建工具(使用.NET语言),方便开发和设计(向导,构建器等等)。这样,一个模块的运行时的环境(如:IDE工具)就成了另一个模块的设计时环境(被开发的源代码)。这里提供了一个使用定制特性很好的例子。IDE工具将会反射你编写的类和类型,然后遵照你的代码行事。不幸的是,由于没有IDE工具的代码,探究这样的例子,已经超出了该文章所阐述的范围。



  标准的.NET特性包含了一个类似的例子。当一个开发者创建自定义控件并把它放到Visual Studio.Net IDE的工具箱中,它们(自定义控件)已经使用了一系列特性,用于说明在属性表单中如何处理自定义控件。Table1列举并描述了在属性表单中用到的4种标准的.NET特性。



  Table 1: 在Visual Studio .NET IDE里设计时属性表单用到的标准的.NET特性.




Attribute
Description
Designer
指定用于为组件实现设计时服务的类。
DefaultProperty
指定在属性表单中,组件的默认的属性。
Category
指定在属性表单中,属性的类别。
Description
指定在属性表单中,有关属性的描述。


  这些与表单相关的特性,让我们认识到,可以在设计时使用特性以及它们的值,就像在运行时一样。

 

自定义特性vs.类的属性

  在特性和类的属性之间存在着明显相似的地方。这给我们何时,何处应该使用自定义特性带来了困惑。开发者们通常引用一个类的属性,并把属性的值作为自己“特性”,那么属性和特性之间真正的区别在哪里呢?

  当你定义特性的时候,它和属性没有根本的区别,使用时,可以以相同的方式把它附加到程序集不同的类型上,而不仅仅在类上使用。Table2列举了可以应用特性的所有程序集类型。

  Table 2:可以应用特性的所有程序集类型。

Type
Assembly
Class
Delegate
Enum
Event
Interface
Method
Module
Parameter
Constructor
Field
Property
ReturnValue
Structure


  让我们从清单中挑选一个作为例子。你可以在参数上应用特性,这看起来很微小,好像是在给参数添加属性?其实,这是一个新颖的,非常不错的主意,因为你不会用类的属性做这件事。这里也突出了特性和属性之间很大的不同之处,因为属性仅仅是类的一个成员而已。它们不能与一个参数,或者清单中列举的其他类型关联起来,当然,这要把类排除在外。



  在另外的方面,类的属性被限制在运行的环境下,而特性却没有被限制。通过定义,一个属性就依赖于特定的类,这个属性仅仅可以通过类的实例访问,或者通过该类派生类的实例访问。另一方面,特性却可以应用到任何地方。在assembly类型上应用特性,以检验是否与自定义特性中的相匹配,这对于assembly类型来说,是最适合的了。在下一部分,我将更多的讨论自定义特性类中的ValidOn属性。在面向组件的开发中,这是非常有用的,因为特性的这个特征将更加促进松耦合。



  特性和属性之间另外的一个不同的地方将涉及到它们各自存储的值。属性成员的值是一个实例化的值,在运行时,是可以被改变的。而特性的值,是在设计时(在源代码里)设定,然后直接把这些特性的值编译成元数据保存到程序集里。之后,你将不能改变这些特性的值。实际上,你已经把这些特性的值,变成硬编码的、只读的数据。



  考虑一下,你应用特性的时候。举个例子,在一个类定义的时候,给其附加了一个特性,那么该类的每一个实例都会拥有相同的分配给此特性值,而不论你实例化该类的多少个实例。你不能把特性附加到一个类的实例上,你只可以在类型/类的定义上应用特性。



  
创建一个自定义特性类



  现在,综合以上的描述,我们将演示一个更实际的实现过程。让我们创建一个自定义特性类。该特性会保存一些关于代码修改的跟踪信息,在源代码里,这些都将作为注释。在这个例子里,我们将仅仅记录一些条目:缺陷id,开发者id,修改的日期,导致缺陷的原因,以及有关修正的注释。为了保持例子足够的简单,我们将关注于如何创建一个自定义特性类(DefectTrackAttribute),而该特性类仅被用于类和方法上。



  DefectTrackAttribute定义的代码如下:




using System;

namespace MyAttributeClasses
{
[AttributeUsage(AttributeTargets.Class|AttributeTargets.Method,AllowMultiple = true)]
public class DefectTrackAttribute :Attribute
{
private string cDefectID ;
private DateTime dModificationDate ;
private string cDeveloperID ;
private string cDefectOrigin ;
private string cFixComment ;

public DefectTrackAttribute ()
{

}

public DefectTrackAttribute(
string lcDefectID,
string lcModificationDate,
string lcDeveloperID )
{
this.cDefectID = lcDefectID ;
this.dModificationDate =
System.DateTime.Parse( lcModificationDate ) ;
this.cDeveloperID = lcDeveloperID ;
}

public string DefectID
{
get { return cDefectID ; }
}

public string ModificationDate
{
get
{
return dModificationDate.ToShortDateString() ;
}
}

public string DeveloperID
{
get { return cDeveloperID ; }
}

public string Origin
{
get { return cDefectOrigin ; }
set { cDefectOrigin = value ; }
}

public string FixComment
{
get { return cFixComment ; }
set { cFixComment = value ; }
}

}
}


  如果你之前没有接触过特性,那么你将对下面的代码有点陌生。




[AttributeUsage(AttributeTargets.Class|AttributeTargets.Method,AllowMultiple = true)]


  这一行代码,把特性[AttributeUsage]附加到特性类的定义上。方括号的语法表明一个特性的构造器被调用。所以,特性类也可以拥有它们自己的特性,这看起来可能有点混淆,但是随着我给你展示可以用特性类来做些什么,你对它的认识,将会越来越清晰。



  [AttributeUsage]特性具有一个定位参数和两个命名参数。定位参数指定了特性类将被用于何种类型,定位参数的值是枚举AttributeTargets的组合。在我的例子里,我仅仅把特性类应用在类和方法上,所以通过组合两个AttributeTargets的值的满足了我的要求。



  [AttributeUsage]特性的第一个命名参数是AllowMultiple,该参数指定了是否可以在同一个类型上应用多次(你所定义的)特性类。默认值是false,即不允许应用多次。但是,根据这个例子的实际情况,你将会在某一类型上不止一次的应用特性(DefectTrackAttribute),所以应该使用[AttributeUsage]的命名参数AllowMultiple,并将其设置为true。这是因为,一个特定的类和方法在其生命周期里会经历多次修订,所以你需要使用[DefectTrackAttribute]特性记录每一次变化。



  [AttributeUsage]特性的第二个命名参数是Inherited,它指定了派生类(使用此特性类的子类)是否继承此特性。我使用了此参数的默认的值false。因为我使用的是默认值,所以也就不需要指定该命名参数。为什么不需要继承呢?我想获取源代码的修改信息是跟每一个具体的类和方法有关的。如果把Inherited设为true,那么开发者将会混淆一个类的[DefectTrackAttribute]特性,无法辨别[DefectTrackAttribute]特性是它自己的还是从父类继承的。



  上面的代码展示了特性类(DefectTrackAttribute)的定义。它继承于System.Attribute,事实上,所有的特性均直接或间接的继承于System.Attribute。



  上面的代码里,还定义了特性的5个私有的字段,这些字段均用于保存与特性相关的值。



  在我们特性类中第一个方法是构造器,它是带有3个参数的签名。构造器的参数对于特性类而言,就是这个特性的定位参数,这些参数是强制性的。如果你愿意,你可以重载构造器,使其可以拥有更多的有关定位参数配置的选择。



  我们的特性类中剩下的部分就是一些公有属性的声明,这些属性与类中的私有字段相对应。当你查阅元数据的时候,你可以使用这些属性访问该特性的值。需要说明的是,对应定位参数的属性没有set语句,只有get语句。这就导致了这些属性是只读的,这也与它们是定位参数而不是命名参数的含义相一致。



 
应用自定义特性

  你现在已经知道在C#代码里,在一个类型声明之前,通过在方括号里使用特性的名字和参数就可以将其附加到目标类型上。



  在下面的代码里,把[DefectTrack]特性附加到一对类和一对方法上。




using System ;
using MyAttributeClasses ;

namespace SomeClassesToTest
{
[DefectTrack( "1377", "12/15/02", "David Tansey" ) ]
[DefectTrack( "1363", "12/12/02", "Toni Feltman",
Origin = "Coding: Unhandled Exception" ) ]
public class SomeCustomPricingClass
{
public double GetAdjustedPrice(
double tnPrice,
double tnPctAdjust )
{ return tnPrice + ( tnPrice * tnPctAdjust ) ; }

[DefectTrack( "1351", "12/10/02", "David Tansey",
Origin = "Specification: Missing Requirement",
FixComment = "Added PriceIsValid( ) function" ) ]
public bool PriceIsValid( double tnPrice )
{ return tnPrice > 0.00 && tnPrice <1000.00 ; }
}

[DefectTrack( "NEW", "12/12/02", "Mike Feltman" ) ]
public class AnotherCustomClass
{
string cMyMessageString ;

public AnotherCustomClass( ){ }

[DefectTrack( "1399", "12/17/02", "David Tansey",
Origin &#61; "Analysis: Missing Requirement" ) ]
public void SetMessage( string lcMessageString )
{ this.cMyMessageString &#61; lcMessageString ; }
}
}


  首先&#xff0c;需要确保你可以访问之前创建的自定义特性&#xff0c;所以需要添加这样一行代码&#xff0c;如下&#xff1a;




using MyAttributeClasses ;


  到此&#xff0c;你就可以使用自定义特性[DefectTrack]装饰或点缀你的类声明和方法了。



  SomeCustomPricingClass有两处地方用到了[DefectTrack]特性。第一个[DefectTrack]特性仅仅使用了三个定位参数&#xff0c;而第二个[DefectTrack]特性还包含了一个命名参数Origin的指定。




[DefectTrack( "1377", "12/15/02", "David Tansey" ) ]
[DefectTrack( "1363", "12/12/02", "Toni Feltman",
Origin &#61; "Coding: Unhandled Exception" ) ]
public class SomeCustomPricingClass
{}


  PriceIsValid()方法也使用了自定义特性[DefectTrack]&#xff0c;并且指定了两个命名参数Origin和FixComment。上述代码包含了[DefectTrack]特性几个额外的用途&#xff0c;你可以检测这些特性。



  一些读者可能会感到惊奇&#xff0c;因为对于源代码修改的信息可以通过使用注释这种传统的做法。
.NET已经使用工具&#xff0c;通过在注释里使用
XML块&#xff0c;把这些信息很好的组织起来。



  在源代码对应的位置&#xff0c;你可以很容易的看到你的注释。你可以通过文本&#xff0c;分析源代码里的注释&#xff0c;从而处理这些信息&#xff0c;但是这个过程是单调冗长的&#xff0c;并且很容易出现错误。.NET提供了工具来处理注释里的XML块&#xff0c;这样可以消除此类问题。



  使用自定义特性可以使你达到同样的效果&#xff0c;它同样提供了一种可以有效组织的方法&#xff0c;用于记录和处理这些信息&#xff0c;并且它还有一个额外的优势。考虑如下情况&#xff0c;当把源代码编译成二进制代码的时候&#xff0c;你是否已经丢失了代码的注释&#xff1f;毫无疑问&#xff0c;注释已经作为副产品&#xff0c;永远的从可执行代码里移出。相比之下&#xff0c;特性的值已经变成了元数据的一部分&#xff0c;永远的绑定到一个程序集里。在没有源代码的情况下&#xff0c;你依然可以访问这些注释信息。



  另外&#xff0c;在源代码里允许特性构造一个与当初在设计时值一样的实例。



  
获取自定义特性的值



  到此&#xff0c;尽管你已经在类和方法上应用了自定义属性&#xff0c;但在实战中你还没有真正的看到它。不管你是否附加了特性&#xff0c;看起来好像什么事情也没有发生。但事实上&#xff0c;事情已经发生了变化&#xff0c;你完全不用理会我的话&#xff0c;你可以用MSIL反编译工具&#xff0c;打开一个包含使用了自定义特性类型的EXE或者DLL文件。MSIL反编译工具能使你看到在IL代码里你定义的特性和它的值。图一是使用ILDASM工具&#xff0c;打开本文中例子编译的EXE文件所看到的。




在Visual C#中定义和使用自己的特性
图一&#xff1a;C#特性


  尽管通过反编译程序集&#xff0c;看到了特性的值&#xff0c;证明了它们的确存在&#xff0c;但是你仍然没有看到跟它们相关的行为。那么现在&#xff0c;你就可以使用反射API遍历一个程序集包含的类型&#xff0c;查询你自定义的特性&#xff0c;在应用了特性的类型上获取特性的值。



  考虑如下测试代码的一般的做法。程序加载指定的程序集&#xff0c;得到一个包含程序集中所有成员的数组&#xff0c;在它们中间&#xff0c;迭代寻找应用了[DefectTrack]特性的类。对于应用了[DefectTrack]特性的类&#xff0c;测试程序将在控制台上输出特性的值。对于类型中的方法&#xff0c;程序仍然采用了同样的步骤和迭代。这些循环采用它们的方式在整个程序集里“游走”。




using System ;
using System.Reflection ;
using MyAttributeClasses ;

public class TestMyAttribute
{
public static void Main( )
{
DisplayDefectTrack( "MyAttributes" ) ;
Console.ReadLine();
}

public static void DisplayDefectTrack(
string lcAssembly )
{
Assembly loAssembly &#61;
Assembly.Load( lcAssembly ) ;

Type[ ] laTypes &#61; loAssembly.GetTypes( ) ;

foreach( Type loType in laTypes )
{
Console.WriteLine("*&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;*" ) ;
Console.WriteLine( "TYPE:/t" &#43;
loType.ToString( ) ) ;
Console.WriteLine( "*&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;*" ) ;

object[ ] laAttributes &#61;
loType.GetCustomAttributes(
typeof( DefectTrackAttribute ),
false ) ;

if( laAttributes.Length > 0 )
Console.WriteLine( "/nMod/Fix Log:" ) ;

foreach( Attribute loAtt in laAttributes )
{
DefectTrackAttribute loDefectTrack &#61;
(DefectTrackAttribute)loAtt ;

Console.WriteLine( "----------------------" ) ;
Console.WriteLine( "Defect ID:/t" &#43;
loDefectTrack.DefectID ) ;
Console.WriteLine( "Date:/t/t" &#43;
loDefectTrack.ModificationDate ) ;
Console.WriteLine( "Developer ID:/t" &#43;
loDefectTrack.DeveloperID ) ;
Console.WriteLine( "Origin:/t/t" &#43;
loDefectTrack.Origin ) ;
Console.WriteLine( "Comment:/n" &#43;
loDefectTrack.FixComment ) ;
}

MethodInfo[ ] laMethods &#61;
loType.GetMethods(
BindingFlags.Public |
BindingFlags.Instance |
BindingFlags.DeclaredOnly ) ;

if( laMethods.Length > 0 )
{
Console.WriteLine( "/nMethods: " ) ;
Console.WriteLine( "----------------------" ) ;
}

foreach( MethodInfo loMethod in laMethods )
{
Console.WriteLine( "/n/t" &#43;
loMethod.ToString( ) ) ;

object[ ] laMethodAttributes &#61;
loMethod.GetCustomAttributes(
typeof( DefectTrackAttribute ),
false ) ;

if( laMethodAttributes.Length > 0 )
Console.WriteLine( "/n/t/tMod/Fix Log:" ) ;

foreach( Attribute loAtt in laMethodAttributes )
{
DefectTrackAttribute loDefectTrack &#61;
(DefectTrackAttribute)loAtt ;
Console.WriteLine( "/t/t----------------" ) ;
Console.WriteLine( "/t/tDefect ID:/t" &#43;
loDefectTrack.DefectID ) ;
Console.WriteLine( "/t/tDeveloper ID:/t" &#43;
loDefectTrack.DeveloperID ) ;
Console.WriteLine( "/t/tOrigin:/t/t" &#43;
loDefectTrack.Origin ) ;
Console.WriteLine( "/t/tComment:/n/t/t" &#43;
loDefectTrack.FixComment ) ;
}
}
Console.WriteLine( "/n/n" ) ;
}
}
}


  让我们来看一下比较重要的几行代码。DisplayDefectTrack()方法的第一行代码和第二行代码得到了加载指定程序集的一个引用并且得到了包含在该程序集中类型的一个数组。




Assembly loAssembly &#61;
Assembly.Load( lcAssembly ) ;

Type[ ] laTypes &#61; loAssembly.GetTypes( ) ;


  使用foreach语句在程序集中的每一个类型上迭代。在控制台上输出当前类型的名称&#xff0c;并使用如下的语句查询当前类型&#xff0c;获取有关[DefectTrack]特性的一个数组。




object[ ] laAttributes &#61;
loType.GetCustomAttributes(
typeof( DefectTrackAttribute ),
false ) ;


  你需要在GetCustomAttributes方法上指定typeof(DefectTrackAttribute) 参数&#xff0c;以限制仅仅返回你创建的自定义特性。第二个参数false指定是否搜索该成员的继承链以查找这些自定义特性。



  使用foreach语句迭代自定义特性数组&#xff0c;并把它们&#xff08;自定义特性&#xff09;的值输出到控制台上。你应该认识到第一个foreach语句块会创建一个新的变量&#xff0c;并且对当前的特性作类型转化。




DefectTrackAttribute loDefectTrack &#61;
(DefectTrackAttribute)loAtt ;


  这一条语句为什么是必须的呢&#xff1f;GetCustomAttributes()方法会返回一个object数组&#xff0c;你为了访问自定义特性的值&#xff0c;所以必须把这些引用转化为它们真正的具体类的引用。转化完以后&#xff0c;你就可以使用这些特性并且可以把特性的值输出到控制台上。



  因为你可以在任意的类和方法上应用特性&#xff0c;因此程序需要调用当前类型上的方法GetMethods()。




MethodInfo[ ] laMethods &#61;
loType.GetMethods(
BindingFlags.Public |
BindingFlags.Instance |
BindingFlags.DeclaredOnly ) ;


  在这个例子里&#xff0c;我给GetMethods()方法传递了一些BindingFlags枚举值。组合使用这三个枚举值&#xff0c;限制仅仅返回在当前的类中直接定义的方法。在这个例子里&#xff0c;之所以这样做&#xff0c;是因为我想限制输出的数量&#xff0c;但是在实际当中&#xff0c;你可能并不需要这样做&#xff0c;因为开发人员可能会在一个重写的方法上应用[DefectTrack]特性。而我的实现代码并没有捕捉应用在这些方法上的特性。



  剩下的代码&#xff0c;从本质上来说&#xff0c;对每一个方法以及每一个类&#xff0c;都在做相同的操作。都是在每一个方法上寻找是否应用了[DefectTrack]特性&#xff0c;如果应用了&#xff0c;就把特性的值输出到控制台上。



  
总结



  在这里&#xff0c;我只是利用一个简单的例子&#xff0c;介绍了开发者如何使用.NET特性提高开发进程。自定义特性有点类似于XML&#xff0c;它最大的好处不在于“它做了什么”&#xff0c;它真正最大的好处在于“你可以用它做什么”。这个是真正无止境的&#xff0c;由于自定义特性本身具有开放的特性&#xff0c;这使得它可以拥有更多新颖的用途。



推荐阅读
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • IhaveconfiguredanactionforaremotenotificationwhenitarrivestomyiOsapp.Iwanttwodiff ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • Webmin远程命令执行漏洞复现及防护方法
    本文介绍了Webmin远程命令执行漏洞CVE-2019-15107的漏洞详情和复现方法,同时提供了防护方法。漏洞存在于Webmin的找回密码页面中,攻击者无需权限即可注入命令并执行任意系统命令。文章还提供了相关参考链接和搭建靶场的步骤。此外,还指出了参考链接中的数据包不准确的问题,并解释了漏洞触发的条件。最后,给出了防护方法以避免受到该漏洞的攻击。 ... [详细]
  • 预备知识可参考我整理的博客Windows编程之线程:https:www.cnblogs.comZhuSenlinp16662075.htmlWindows编程之线程同步:https ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • Metasploit攻击渗透实践
    本文介绍了Metasploit攻击渗透实践的内容和要求,包括主动攻击、针对浏览器和客户端的攻击,以及成功应用辅助模块的实践过程。其中涉及使用Hydra在不知道密码的情况下攻击metsploit2靶机获取密码,以及攻击浏览器中的tomcat服务的具体步骤。同时还讲解了爆破密码的方法和设置攻击目标主机的相关参数。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 本文介绍了UVALive6575题目Odd and Even Zeroes的解法,使用了数位dp和找规律的方法。阶乘的定义和性质被介绍,并给出了一些例子。其中,部分阶乘的尾零个数为奇数,部分为偶数。 ... [详细]
  • ALTERTABLE通过更改、添加、除去列和约束,或者通过启用或禁用约束和触发器来更改表的定义。语法ALTERTABLEtable{[ALTERCOLUMNcolu ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
author-avatar
无声无无为爱
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有