找回密码
 注册

QQ登录

只需一步,快速开始

查看: 5439|回复: 9

[转帖]Microsoft Visual Studio .NET编程资料

XUXINXIN  | 发表于 2005-7-8 16:23:49 | 显示全部楼层 |阅读模式 来自 中国天津

马上注册,结交更多好友,享用更多功能。

您需要 登录 才可以下载或查看,没有帐号?注册

x
项目调试时候,出现其中用到的一个组件“访问被拒绝”的解决方法。
有时候,我们在调试ASP.net程序的时候,会很痛苦的碰到。某某组件访问被拒绝。这时候重起IIS,电脑注销都不起作用。这时候的错误信息一般类似如下的方式:

“/”应用程序中的服务器错误。
--------------------------------------------------------------------------------
配置错误
说明: 在处理向该请求提供服务所需的配置文件时出错。请检查下面的特定错误详细信息并适当地修改配置文件。
分析器错误信息: 访问被拒绝:“CSDN.Authentication”。
源错误:
行 196:    <add assembly="System.EnterpriseServices, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
行 197:    <add assembly="System.Web.Mobile, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
行 198:    <add assembly="*"/>
行 199:   </assemblies>
行 200:  </compilation>

源文件: c:\windows\microsoft.net\framework\v1.1.4322\Config\machine.config    行: 198
程序集加载跟踪: 下列信息有助于确定程序集“CSDN.Authentication”无法加载的原因。
=== Pre-bind state information ===
LOG: DisplayName = CSDN.Authentication
(Partial)
LOG: Appbase = file:///F:/MyDevelop/AD/Code/ADManageWeb
LOG: Initial PrivatePath = bin
Calling assembly : (Unknown).
===
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Post-policy reference: CSDN.Authentication
LOG: Attempting download of new URL file:///C:/WINDOWS/Microsoft.NET/Framework/v1.1.4322/Temporary ASP.NET Files/root/3151f96f/581ce000/CSDN.Authentication.DLL.
LOG: Attempting download of new URL file:///C:/WINDOWS/Microsoft.NET/Framework/v1.1.4322/Temporary ASP.NET Files/root/3151f96f/581ce000/CSDN.Authentication/CSDN.Authentication.DLL.
LOG: Attempting download of new URL file:///F:/MyDevelop/AD/Code/ADManageWeb/bin/CSDN.Authentication.DLL.
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Post-policy reference: CSDN.Authentication, Version=1.0.1965.30523, Culture=neutral, PublicKeyToken=null
--------------------------------------------------------------------------------
版本信息: Microsoft .NET Framework 版本:1.1.4322.2300; ASP.NET 版本:1.1.4322.2300

其中最常见的原因是Indexing service服务引起的。解决方法就是停用Indexing service,或配置ASP.net 的临时目录不受Indexing service服务的影响。

配置ASP.net 的临时目录不受Indexing service服务的影响的步骤如下:

1、开始 -- 管理工具 -- 计算机管理(Win2003的位置,其他操作系统类似)打开计算机管理
2、展开计算机管理左边树中“服务和应用程序”节点,再在其下展开“索引服务”节点,再在其下展开“System”节点,再在其下展开“目录”节点。
3、在计算机管理的右边我们可以看到配置的索引服务目录。
4、在“目录”节点上右击鼠标,选择“新建”--“目录”
5、在“添加目录”对话框中,路经输入框中输入 ASP.net 的临时文件目录。默认应该是:
c:\<WINDIR>\Microsoft.NET\Framework\<Version Number>\Temporary ASP.NET Files  目录。
<Version Number> 指你要处理的.net版本。
6、在“包含在索引中吗?”选项中,选择“否”
7、单击“确定”按钮
8、在“索引服务”节点上右键单击,重起索引服务即可。

参考以下文档:
http://community.csdn.net/Expert/topic/4075/4075120.xml
http://support.microsoft.com/default.aspx?scid=kb;en-us;329065
http://
[此贴子已经被作者于2005-7-10 11:24:16编辑过]

XUXINXIN 发表于 2005-7-8 16:27:14 | 显示全部楼层 来自 中国天津
我们都知道.NET Framework 2.0支持了泛型,在它给我们带来性能收益和强类型的方便的同时,由于接口、委托等元素对泛型的支持,一些前所未有的泛型设计和算法也逐渐展现出来。
C# 2.0支持了一种叫“匿名方法”的特性,它与泛型委托结合之后,能够产生出许多诱人的新用法。比如,若想找出列表中所有大于10的元素,只需要:
List<int> l1 = new List<int>();
//populate l1

List<int> l2 = l1.FindAll(delegate(int i)...{ return i > 10; });
对于首次接触这个新特性的人来说,此语法较为费解。我们看看Generic List类的FindAll方法的定义:
public List<T> FindAll(
    Predicate<T> match
);
我们看到,唯一的参数match是“Predicate<T>”类型的,那Predicate<T>又是什么呢,请看定义:
public delegate bool Predicate<T>(
    T obj
);
这是一个委托,接受T类型的一个参数obj,并返回一个布尔类型,表示对obj的一个判断。现在我们就明白了,FindAll方法本应接受一个函数委托作为参数,以表示查找满足判断的所有元素,而C#的匿名方法则提供了一种就地提供函数委托的方法,使得委托的逻辑可以在同一条语句中表达。
除了Generic List,System.Array和许多其它泛型容器都提供了这种接受泛型委托的方法,在支持匿名方法的C#中,当然如鱼得水,发挥最大的作用。而VB(和多数其它的.NET语言)不支持匿名方法,在使用此类接受泛型委托的方法时,就必须单独创建一个函数,大大削弱了可读性,使用此类方法的优势也就不复存在。为了弥补此缺陷,让VB也能享受这类方法的便利,我们提出“谓词”的概念。其实“谓词”就是“判断”的一个更加名词化的说法。我们试图将常用的判断函数封装成类,使得他们可以被反复重用。首先定义一个封装Generic System.Predicate委托的类:UnaryPredicate(Of T)作为一元谓词的基类。
Public MustInherit Class UnaryPredicateClass UnaryPredicate(Of T)
    Public Shared Widening Operator CType(ByVal predicate As UnaryPredicate(Of T)) As Predicate(Of T)
        Return AddressOf predicate.PredicateProcedure
    End Operator

    Protected MustOverride Function PredicateProcedure()Function PredicateProcedure(ByVal obj As T) As Boolean
End Class
我们看到这个类定义了一个与Generic Predicate委托签名相同的虚函数,用于子类实现相关的逻辑。注意,UnaryPredicate定义了一个CType运算符,将一元谓词的实例隐式转化为Generic Predicate委托。这个就是确保我们的谓词可以用在原先Generic Predicate的上下文中。
接下来我们就要实现具体的谓词。首先我们想表达关于比较的谓词,比如大于、小于、等于和不等于等等。他们都十分相似,我们看一个表示大于的谓词如何实现:
Public Class GreaterThanClass GreaterThan(Of T As IComparable(Of T))
    Inherits UnaryPredicate(Of T)

    Private m_Value As T

    Public Sub New()Sub New(ByVal value As T)
        m_Value = value
    End Sub

    Protected Overrides Function PredicateProcedure()Function PredicateProcedure(ByVal obj As T) As Boolean
        Return obj.CompareTo(m_Value) > 0
    End Function
End Class
关键点就在于此类重写PredicateProcedure的逻辑,它表示传入的参数是否大于我们谓词自己的参数。同样,还可以定义出等于、小于等所有比较判断的谓词。
现在,我们就可以在泛型List上使用我们的谓词了,假设实现和刚才C#一样的任务:找出所有大于10的元素。
Dim l1 As New List(Of Integer)
For i As Integer = 1 To 100
    l1.Add(i)
Next

Dim l2 As List(Of Integer)
l2 = l1.FindAll(New GreaterThan(Of Integer)(10))
注意最后一句,这就是我们谓词的用法,只要传入一个谓词的实例,就会被我们的基类自动转化为Generic Predicate并完成计算。而且谓词是类,因此比匿名方法有更好的重用性,并有可能达成更广泛的设计。而且这种使用谓词的语法具有相当高的可读性。
XUXINXIN  | 发表于 2005-7-9 14:24:37 | 显示全部楼层 来自 中国天津
根据InfoPath team blog里介绍的File Attachment Control的内容,写了两个类来处理文件的编码与解码。
    public class InfoPathAttachmentEncoder
    ...{
        private string base64EncodedFile = string.Empty;
        private string fullyQualifiedFileName;

        /**//// <summary>
        /// Creates a encoder to create an InfoPath attachement string
        /// </summary>
        /// <param></param>
        public InfoPathAttachmentEncoder(string fullyQualifiedFileName)
        ...{
            if (fullyQualifiedFileName == string.Empty)
                throw new ArgumentException("Must specify file name", "fullyQualifiedFileName");

            if (!File.Exists(fullyQualifiedFileName))
                throw new FileNotFoundException("File does not exist: " + fullyQualifiedFileName, fullyQualifiedFileName);

            this.fullyQualifiedFileName = fullyQualifiedFileName;
        }

        /**//// <summary>
        /// Returns a Base64 encoded string
        /// </summary>
        /// <returns>String</returns>
        public string ToBase64String()
        ...{
            if (base64EncodedFile != string.Empty)
                return base64EncodedFile;

            // This memory stream will hold the InfoPath file attachment buffer before Base64 encoding
            MemoryStream ms = new MemoryStream();

            // get the file information
            using (BinaryReader br = new BinaryReader(File.Open(fullyQualifiedFileName, FileMode.Open, FileAccess.Read, FileShare.Read)))
            ...{
                string fileName = Path.GetFileName(fullyQualifiedFileName);

                uint fileNameLength = (uint)fileName.Length + 1;

                byte[] fileNameBytes = Encoding.Unicode.GetBytes(fileName);

                using (BinaryWriter bw = new BinaryWriter(ms))
                ...{
                    // Write InfoPath attachment signature
                    bw.Write(new byte[] ...{ 0xC7, 0x49, 0x46, 0x41 });

                    // Write the default header information
                    bw.Write((uint)0x14);    // size
                    bw.Write((uint)0x01);    // version
                    bw.Write((uint)0x00);    // reserved

                    // Write the file size
                    bw.Write((uint)br.BaseStream.Length);

                    // Write the size of the file name
                    bw.Write((uint)fileNameLength);

                    // Write the file name (Unicode encoded)
                    bw.Write(fileNameBytes);

                    // Write the file name terminator (which is two nulls in Unicode)
                    bw.Write(new byte[] ...{0,0});

                    // Iterate through the file reading data and writing it to the outbuffer
                    byte[] data = new byte[64*1024];
                    int bytesRead = 1;

                    while (bytesRead > 0)
                    ...{
                        bytesRead = br.Read(data, 0, data.Length);
                        bw.Write(data, 0, bytesRead);
                    }
                }
            }


            // This memorystream will hold the Base64 encoded InfoPath attachment
            MemoryStream msOut = new MemoryStream();

            using (BinaryReader br = new BinaryReader(new MemoryStream(ms.ToArray())))
            ...{
                // Create a Base64 transform to do the encoding
                ToBase64Transform tf = new ToBase64Transform();

                byte[] data = new byte[tf.InputBlockSize];
                byte[] outData = new byte[tf.OutputBlockSize];

                int bytesRead = 1;

                while (bytesRead > 0)
                ...{
                    bytesRead = br.Read(data, 0, data.Length);

                    if (bytesRead == data.Length)
                        tf.TransformBlock(data, 0, bytesRead, outData, 0);
                    else
                        outData = tf.TransformFinalBlock(data, 0, bytesRead);

                    msOut.Write(outData, 0, outData.Length);
                }
            }

            msOut.Close();
            
            return base64EncodedFile = Encoding.ASCII.GetString(msOut.ToArray());
        }
    }
    public class InfoPathAttachmentDecoder
    ...{
        private const int SP1Header_Size = 20;
        private const int FIXED_HEADER = 16;

        private int fileSize;
        private int attachmentNameLength;
        private string attachmentName;
        private byte[] decodedAttachment;

        /**//// <summary>
        /// Accepts the Base64 encoded string
        /// that is the attachment
        /// </summary>
        public InfoPathAttachmentDecoder(string theBase64EncodedString)
        ...{
            byte [] theData = Convert.FromBase64String(theBase64EncodedString);
            using(MemoryStream ms = new MemoryStream(theData))
            ...{
                BinaryReader theReader = new BinaryReader(ms);            
                DecodeAttachment(theReader);
            }
        }

        private void DecodeAttachment(BinaryReader theReader)
        ...{
            //position the reader to get the filesize
            byte[] headerData = new byte[FIXED_HEADER];
            headerData = theReader.ReadBytes(headerData.Length);

            fileSize = (int)theReader.ReadUInt32();
            attachmentNameLength = (int)theReader.ReadUInt32() * 2;
            
            byte[] fileNameBytes = theReader.ReadBytes(attachmentNameLength);
            //InfoPath defaults to UTF8 encoding.
            Encoding enc = Encoding.Unicode;
            attachmentName = enc.GetString(fileNameBytes, 0, attachmentNameLength - 2);
            decodedAttachment = theReader.ReadBytes(fileSize);
        }

        public void SaveAttachment(string saveLocation)
        ...{
            string fullFileName = saveLocation;
            if(!fullFileName.EndsWith(Path.DirectorySeparatorChar.ToString()))
            ...{
                fullFileName += Path.DirectorySeparatorChar;
            }

            fullFileName += attachmentName;

            if(File.Exists(fullFileName))
                File.Delete(fullFileName);
            
            FileStream fs = new FileStream(fullFileName, FileMode.CreateNew);
            BinaryWriter bw = new BinaryWriter(fs);
            bw.Write(decodedAttachment);

            bw.Close();
            fs.Close();
        }

        public string Filename
        ...{
            get...{ return attachmentName; }
        }

        public byte[] DecodedAttachment
        ...{
            get...{ return decodedAttachment; }
        }
    }   
这样,我们就能根据需要对File Attachment Control来做进一步的控制了。
XUXINXIN  | 发表于 2005-7-9 14:25:59 | 显示全部楼层 来自 中国天津
派生类无法作为ref参数
首先我们来看一段代码:
using System;
class Class1 ...{
    static void Main(string[] args)...{
        DeriveClass d = new DeriveClass();
        Do(ref d);
    }
    public static void Do(ref BaseClass b)...{}
}

class BaseClass ...{}
class DeriveClass : BaseClass ...{}


这段代码我们在编译的时候,会产生如下异常:
F:\MyDevelop\Tests\ConsoleApplication2\Class1.cs(8): 与“Class1.Do(ref BaseClass)”最匹配的重载方法
具有一些无效参数
F:\MyDevelop\Tests\ConsoleApplication2\Class1.cs(8): 参数“1” : 无法从“ref DeriveClass”转换为“ref BaseClass”
我们先来看解决方法,先不分析原因:
几种解决方法如下:
解决方法一,在传递前,先把派生类转换为基类

using System;
class Class1...{
    static void Main(string[] args)...{
        DeriveClass d = new DeriveClass();
        BaseClass b = d;
        Do(ref b);
    }

    public static void Do(ref BaseClass b)...{}
}

class BaseClass ...{}
class DeriveClass : BaseClass ...{}

解决方法二:不使用ref参数。

using System;
class Class1...{
    static void Main(string[] args)    ...{
        DeriveClass d = new DeriveClass();
        Do(d);
    }
    public static void Do(BaseClass b)    ...{}
}

class BaseClass ...{}
class DeriveClass : BaseClass ...{}
分析原因(个人分析):
如果要使用ref向一个函数传递进入一个类,我们就可以在这个函数体内,new 一个新的对象,这个参数出来函数体后,仍然被指向那个新的对象。

如果最初的代码可以编译通过的话,就会产生一种情况:
一个派生类的指针被传递进入函数,函数体内用户new 了一个基类,并把上述原来指向派生类的指针指向一个基类。这时候,在外部函数体后面的代码,可能就会出问题。为了避免这个问题,编译器就不让你编译通过最上面的代码。
当然你可以通过解决方法一的方式,欺骗编译器。以编译通过。但是这时候,一定要小心出现上述说的:指向派生类的指针指向一个基类带来的问题。
XUXINXIN  | 发表于 2005-7-9 14:27:45 | 显示全部楼层 来自 中国天津

象Google的广告,展示啥内容,都是由Google自己控制的,使用普通的页面提交连接的方式,我们是无法统计我们页面上的Google广告被点击了多少次,被谁点击了。因为这些页面都不受我们控制。
下面介绍一个可以统计Google那样广告点击次数的方法。
<html><head>
    <title>点击计数</title>
   
  1. &lt;script language="javascript"&gt;<br>
  2.     <!--
  3.     function keyDown(e)
  4.    {
  5.         if (event.srcElement.tagName=='IFRAME'
  6.         && event.clientX==-1
  7.         && event.clientY==-1
  8.         && event.fromElement=='[object]'){
  9.             // 如果需要向其他页面提交
  10.             //document.adform.submit();
  11.             var num = parseInt(shownum.innerHTML);
  12.             shownum.innerHTML = num + 1;
  13.         }
  14.         return false;
  15.     }
  16. document.onmousemove=keyDown;
  17. document.onmouseover=keyDown ;
  18. document.onmouseout=keyDown ;
  19. document.onblur=keyDown;
  20.     //-->
  21.     <br>&lt;/script&gt;
复制代码

</head><body><!-- 如果需要向另外一个页面传递参数,可以用下面的方式//-->
    <form>
        <input>
        <input>
            <input>
            </form>
   
        
<!--页面提交代码结束//-->        

    点击IFrame中的次数:<div>
        0</div>
   
   
    <br />
    <br />
    <a href="http://blog.joycode.com/" target="_blank" >本页其它连接</a></body></html>


上述代码中,我们在点击、移动等事件中,判断用户点击的是不是某个需要的范围内。然后进行计数,如果我们需要额外的记录,可以在这些事件函数中,向一个我们可控的页面进行提交。为了不影响页面的展示,这个页面被提交的页面,是在一个隐含的IFrame中实现的,具体看上述代码就明白了。
通过以上的方法,我们就可以实现不论点本网站的自己广告,还是Google广告,每点击一次,增加多少可用分这类的逻辑了。(当然这个逻辑可以更复杂)
[此贴子已经被作者于2005-7-9 14:30:14编辑过]

XUXINXIN  | 发表于 2005-7-9 14:31:54 | 显示全部楼层 来自 中国天津
.net 2.0 中 DataTable 小兵变大将
我们先看一段WEB Service的代码。
[WebMethod]
public DataTable GetInfo()
...{
    OleDbConnection nwindConn = new OleDbConnection(
    "rovider=Microsoft.Jet.OLEDB.4.0;" +
    "Data Source=D:\\Northwind\\northwind.mdb;");

    OleDbCommand selectCMD =
    new OleDbCommand("SELECT CustomerID, CompanyName FROM Customers"
    , nwindConn);
    selectCMD.CommandTimeout = 30;

    OleDbDataAdapter custDA = new OleDbDataAdapter();
    custDA.SelectCommand = selectCMD;

    DataSet custDS = new DataSet();
    custDA.Fill(custDS, "Customers");
    return custDS.Tables[0];
}

在.net 1.1 中,这是典型的一个错误,在.net 1.1 、1.0中,WEB Service 的返回或者输入参数不能是 DataTable,这是一个众人皆知的知识点。原因就是 DataTable 不象DataSet那样支持序列化。在.net 1.1中,我们解决这个问题的方法就是使用DataSet。但是使用DataSet 的时候,经常会有一种杀鸡用牛刀的感觉。
附:.net 1.1 中使用DataTable作为WEB Service 返回值会报以下异常:
类型 System.ComponentModel.ISite 的成员 System.ComponentModel.MarshalByValueComponent.Site 是接口,因此无法将其序列化。

在.net 2.0 中,以上同样的代码,则没有任何问题了。原因是2.0中 DataTable实现了序列化、反序列。
在VS2005 Beta2 的文档中,我们可以看到2.0 中 DataTable实现了以下接口:

Explicit Interface Implementations
  System.ComponentModel.IListSource.get_ContainsListCollection   
  System.ComponentModel.IListSource.GetList   
  System.Xml.Serialization.IXmlSerializable.GetSchema   
  System.Xml.Serialization.IXmlSerializable.ReadXml   
  System.Xml.Serialization.IXmlSerializable.WriteXml  
而在1.1中,DataTable 只实现了一个接口:
Explicit Interface Implementations
System.ComponentModel.IListSource.ContainsListCollection
把DataSet中的一些功能移到 DataTable中,2.0 中还有 Merge 方法,即合并数个数据集。
DataTable的代码合并参看下面代码。
  private static void DemonstrateMergeTable()
  ...{
    DataTable table1 = new DataTable("Items");

    DataColumn column1 = new DataColumn("id", typeof(System.Int32));
    DataColumn column2 = new DataColumn("item", typeof(System.Int32));
    table1.Columns.Add(column1);
    table1.Columns.Add(column2);

    table1.PrimaryKey = new DataColumn[] ...{ column1 };
    table1.RowChanged += new System.Data.DataRowChangeEventHandler(Row_Changed);

    DataRow row;
    for (int i = 0; i <= 3; i++)
    ...{
      row = table1.NewRow();
      row["id"] = i;
      row["item"] = i;
      table1.Rows.Add(row);
    }

    // Accept changes.
    table1.AcceptChanges();

    DataTable table2 = table1.Clone();
    row = table2.NewRow();
    row["id"] = 14;
    row["item"] = 774;
    table2.Rows.Add(row);

    row = table2.NewRow();
    row["id"] = 12;
    row["item"] = 555;
    table2.Rows.Add(row);

    row = table2.NewRow();
    row["id"] = 13;
    row["item"] = 665;
    table2.Rows.Add(row);

    // Merge table2 into the table1.
    table1.Merge(table2);
  }
综合上述,.net 2.0 中 DataTable 从后台的默默无问的小兵变成独当一面的大将了。
XUXINXIN  | 发表于 2005-7-9 14:34:30 | 显示全部楼层 来自 中国天津
白话面向智能体编程(Agent Oriented Programmig, AOP)之一
Adrian同志有一篇关于Aspect Oriented Programming的Blog The Ted Neward Challenge (AOP without the buzzwords),俺非常的喜欢。这篇文章中文翻译过来可以是“无废话AOP”,它从一个脱离了具体实现手段的视角,用简单清晰的语言向我们表达了Aspect Oriented Programming的本意:尽量将需求和实现的关系由1:N逼近至1:1。俺也非常希望能整出这么一个“无废话Agent Oriented Programming”,可是基于下面两点考虑,俺还是决定放弃:一个是Agent Oriented Programming不光是个有意思的技术话题,还与学术界,理论界有很深的渊源。要“无废话”,且饶有趣味地阐述这样一个理论问题,是相当有难度地,还是时不时有必要来点废话,才不至于让同志们看得睡着过去。另一方面,记得上学的时候,老师传授的读书体验是先把书读厚,再把书读薄。书读薄了,吃透了,归纳出来,写出来的都是精华,当然也就无废话;而俺现在还处于把书读厚的第一阶段,所以写出来废话会比较多一点。基于这样的考虑,这个系列的文章可以被称为是“白话Agent Oriented Programming”。


如果俺有足够的耐性把文章全整出来and您有足够的耐性看完所有文章,您会对以下的内容有一个大概的了解:

l         什么是Agent和Agent Oriented Programming
Agent这个概念并不是空穴来风,在理论界对它的研究已经很深入,Agent是Object的进化版本,进化的目的是为了让软件系统更贴近现实世界。从程序设计的角度理解,可以认为Agent就是绑定了Thread的Object。
l         为什么要整这么个概念出来,这个东东和其他一堆泛滥的概念的关系是什么
可以说搞软件的人,特别是搞平台软件的人,本质上都比较懒,整天琢磨着怎么更懒,如果有个东东可以让100行的代码压缩到10行,将设计,调试,维护的时间由1天压缩为1个小时,他们就会将这种方法拎出来,凑成一个概念。OOP,AOP,SOA什么的基本上可以说都是这么整出来的(戏言戏说)。
l         怎么样在.Net 平台上实现Agent Oriented Programming
Agent Oriented Programming是一种思想,与平台无关。但既然是准备贴在以微软技术为主题的博客堂上,为了以示尊重,还是选择在.Net平台上实现的比较好。
l         俺们设计的一个Agent SDK,及其一些蛮有趣的特性
实际上这个Agent SDK是俺们设计一个工作流引擎的副产品。在给大家做演示的时候,这个SDK倒是比上层的工作流引擎更能引起大家的兴趣(除了胸闷还是胸闷)SDK部分地实现了Agent的一些特性,例如AgentMethod,TimerMethod,Sensebility等。
l         未来之路
这里的未来之路包含两个意思。一层意思是,围绕着Agent Oriented Programming,还有很多高级的特性等待我们去实现,例如移动Agent,合作Agent等。另一方面,对于任何思想,技术而言,光掌握其原理,明白是怎么回事,是不够的,还是得运用到项目中,创造效益,老板才会高兴,才会去下力气去推广。所以俺们也要考虑如何将这么好的东东应用到实际的项目开发中去。
XUXINXIN  | 发表于 2005-7-9 14:37:44 | 显示全部楼层 来自 中国天津
白话面向智能体编程(Agent Oriented Programmig, AOP)之二
Agent之前-Object世界

在说起Agent之前,俺们还有必要先敬拜一下Agent的前辈Object,因为Agent实际上是由Object“进化”而来的。这话说出来,可能有些读者同志不太高兴了,Object有什么不好吗?现在这么多复杂的系统,不都是基于OO的思想设计出来的吗?

然也,OO的确为提高软件开发效率做出了很大的贡献,但是在使用过程中,OO也暴露出了一些痒处:

痒处一:OO并没有对现实世界中的实体加以区分。
在OO世界中,所有的软件实体都是Object,现实世界中的一张发票和一为员工,映射到OO世界中都是一个Class。Class发票具有一些数据(日期,金额)和操作(效验,保存),Class员工也具有一些数据(姓名,职位)和操作(上班,下班),从映射的角度来看,任何现实世界的实体都是数据和操作的集合。但实际上,在现实世界中,发票和员工还是有区别的。区别在哪里呢?在于发票是一个物体, 而员工是一个有心智的实体。发票类的方法只能是被动地被调用,如果我们不调用,任何一张发票都不会自动的进行效验或者保存。而员工的方法调用与否,是由员工自己来决定的。今天生病了, 不高兴上班,上班操作就不会被执行,今天任务重,他就会自动执行加班这个操作。换句话说员工类的操作不是被动调用的,而是自发完成的。
这种只能被动调用和自发执行的区别,归结一下其原因,是因为员工具有自己的心智,而发票是没有的。传统的OO并没有引入这个区分,而这种区分的却失所造成的结果就是所有的操作都是被动地等待调用。虽然我们也可以引入计时器,或者多线程技术来模拟主动操作,但这种并不完全贴合现实世界的设计思路是不是让各位隐约感觉有些不爽呢。

痒处二:同步和异步被人为地剥离。
上面讲的是OO对有些东东没有区分,这点说的是OO对某些东东又多余地加以了区分。比如,现实世界中,老师对同学们说:“请把书翻到78页。”老师并不需要知道翻书这个操作对于同学们来说是同步操作还是异步操作,他并没有说“请把书同步地/异步地翻到78页。”换句话说,翻书这个操作是同步还是异步,对于调用者(老师)来说,是不需要知道的,同学们知道就OK了。但是影射到OO世界中,对于调用者来说,他在调用的时候,就必须知道是同步调用还是异步调用。归纳起来说,在现实世界中,调用方式这种知识,是被调用方拥有的,而调用方是不需要考虑的,但在OO世界中,这个知识由被调用方转移到了调用方。Something is different。体会一下,这种差异会带来什么样的问题。

痒处三:无法自然地模拟现实世界中的感知能力(Sensebility)。
举个烧菜的例子来说明感知能力。老妈教俺烧菜的时候,常用的语法是:“当什么什么的时候,就怎么怎么样”。比如:当水烧开的时候,把肉放进去;当鸡块炸至金黄色的时候,捞出锅来。这就是感知能力的例子。鸡块作为感知源,它的属性可以发生变化;俺作为感知器,可以捕捉到鸡块的颜色这个属性的变化。如果鸡块的颜色由肉色转变至金黄色,俺就必须做出相应的操作/处理:把鸡块捞出锅来。

这种感知能力在现实世界中是非常普遍地存在的,然而映射到OO世界中来,却显的有些别扭。如果我们将鸡块和人构造为两个Class,要想人能感知到鸡块上某个属性值的变化,首先想到的是使用观察者(Observer)模式,在.Net平台下呢,则是在鸡块类中构造一个delegate,然后将人的某个操作挂到这个delegate上,当鸡块颜色值发生变化的时候,触发这个delegate。这种方式至少存在三个让人感觉别扭的地方:

l         鸡块就是鸡块,为什么鸡块这个Class里面要包含一个额外的delegate呢,这个delegate的存在对于实现鸡块这个class本身的逻辑来说没有任何意义,对于鸡块的逻辑而言,这个delegate是完全多余的,这还只是颜色属性上的delegate,不难想像,如果外界还有其他Class需要感知鸡块类其他属性上的变化,会有更多的delegate,更多的与鸡块本身逻辑不相干的代码出现。

l         注意到一个比较细节的问题。俺在看到鸡块颜色变为金黄色后,执行的操作是把鸡块捞出锅。这个“捞出锅”的操作,是由俺的心智来完成的,不是受鸡块的心智指挥,它自己蹦出锅来的。而在上面提到的使用delegate的解决方案中,我们把“捞出锅”这个操作挂载到delegate上,则“捞出锅”是由鸡块当前占用的线程来执行的,如果将线程理解为心智的话,则意味着是由鸡块的心智控制着人将自己捞出锅。是不是很诡异J  更直白地说法是:应该有一个线程来处理鸡块颜色的变化,另外再有一个线程来收到颜色变化的通知,并执行“捞出锅”的操作。这里有同志应该说了:也好办,把delegate的机制修改为多线程的就OK了!的确是这样,但需要注意的是要保持多线程机制对鸡块和人的透明性,如果因为需要贴近现实世界而增加Class的复杂性,那也违背了我们的初衷。

l         最后一个问题比较有意思。在现有的delegate解决方案中,我们只能针对实例(Instance)进行注册,而不可以针对类型(Type)进行注册。什么概念呢?比如锅里面有十个鸡块,俺得把“捞出锅”这个操作到这十个鸡块上依次注册一遍,才能保证每个鸡块在适当的时机被捞出来。是不是觉得和现实世界中的实际情况有些出入J 如果能够提供针对类型的注册机制,只要将俺的后续操作到鸡块类上注册一次,在感知范围内的所有鸡块,管他是十块还是二十块,都能被俺感知到颜色上的变化并执行正确的后续操作,这样会来的更简洁,更自然。

上面罗罗嗦嗦给OO挑了些毛病。实际上归纳起来就一句话:OO并不是对现实世界最贴切地模拟。而软件世界的终极目标是为了模拟现实世界,既然OO并不是最贴切的模拟,问题也就暴露出来了,改进的余地也就显现出来了。
     怎么改进?轮到Object的接班人Agent出场了。
XUXINXIN  | 发表于 2005-7-9 14:50:39 | 显示全部楼层 来自 中国天津
调用Object.GetHashCode的缺省实现
所有的.NET类都是基于System.Object类的。在Object中定义了返回值为int的虚函数GetHashCode。因为是虚函数,子类可以重写(override)GetHashCode以体现更合逻辑的算法。但是,重写后的GetHashCode可能会产生比较集中占用了Int32的狭窄区间的Hash Code(哈希值),使得更多的不同实例有着相同的值.

这样的实现一点也不违背长生Hash code的原则。在极端情形下即使所有的实例的哈希值都是相同的,这也是合法的算法。当然也是很差的算法。

而Object.GetHashCode的缺省实现则有着很好的哈希值分布。在实践中人们常发现一个实例的Hash Code几乎可以有和这个实例有一一对应的关系。也就是说,这是一个很佳的哈希算法。

问题是,当GetHashCode被子类重写后,我们如何能够在需要的情形下调用Object.GetHashCode的缺省实现呢?答案是:
int System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(object o); 这是定义在程序集mscorlib之中的。
XUXINXIN  | 发表于 2005-7-9 14:52:29 | 显示全部楼层 来自 中国天津
VS2005中用firefox访问 Asp.net Development Server 的站点的方法。
今天在测试vs2005的TreeView控件显示的代码,在非IE浏览器下效果的时候,发现:
当你用firefox访问 Asp.net Development Server 的站点(也就是vs2005自带的那个开发用web站点),会让你输入用户帐号和密码,但是你不论输入那个帐号都是拒绝访问的解决方法。
搜索Google,发现今年4月份的时候,就有人向微软报告了这个bug,对应的也有解答。
解决方法:
解决方法参看下面链接。
Bug Details: asp.net Development Server need identification with Firefox on non IE-browser
http://lab.msdn.microsoft.com/productfeedback/viewfeedback.aspx?feedbackid=582fcb60-47cd-4475-869b-130786c0821d
具体来说就是在Firefox地址栏中输入:about:config
然后在过滤器中输入:NTLM
这时候我们在过滤的结果中,可以看到只有两项。
其中一项是:network.automatic-ntlm-auth.trusted-this
双击这一项,在输入值中输入 localhost
这里输入的为Asp.net Development Server的站点名。
关闭FireFox,重新访问Asp.net Development Server的站点就可以解决这个问题了。
原因:
ASP.Net development web server 对于每个请求,都会执行 NTLM 验证,以确定处理的是来自同一个用户的请求。
在IE中这是自动完成的,Firefox浏览器需要做以上设置才可以。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表