[C#]Net 3.5 用DataContractJsonSerializer实现Json序列和反序

mikel阅读(773)

近来在研究JQuery,用$.ajax在与后台交换数据时用到Json格式,后台与前台序列化和反序列化方法研究了很久才成功,发文共享.

前台调用的JS文件需要用到JQuery-1.2.6.jsjson2.js,后台引用空间System.Runtime.SerializationSystem.Runtime.Serialization.Json

Json序列化和反序列化方法

        /// <summary>
        
/// Json序列化,用于发送到客户端
        
/// </summary>
        public static string ToJsJson(this object item)
        {
            DataContractJsonSerializer serializer 
= new DataContractJsonSerializer(item.GetType());
            
using (MemoryStream ms = new MemoryStream())
            {
                serializer.WriteObject(ms, item);
                StringBuilder sb 
= new StringBuilder();
                sb.Append(Encoding.UTF8.GetString(ms.ToArray()));
                
return sb.ToString();
            }
        }
        
/// <summary>
        
/// Json反序列化,用于接收客户端Json后生成对应的对象
        
/// </summary>
        public static T FromJsonTo<T>(this string jsonString)
        {
            DataContractJsonSerializer ser 
= new DataContractJsonSerializer(typeof(T));
            MemoryStream ms 
= new MemoryStream(Encoding.UTF8.GetBytes(jsonString));
            T jsonObject 
= (T)ser.ReadObject(ms);
            ms.Close();
            
return jsonObject;
        }

实体类

    [DataContract]
    
public class TestObj
    {
        [DataMember]
        
public string make { getset; }
        [DataMember]
        
public string model { getset; }
        [DataMember]
        
public int year { getset; }
        [DataMember]
        
public string color { getset; }
    }

——————————————–JavaScript获取Json————————————————–

JavaScript调用测试代码

$('#getJson').click(function() {
                $.ajax({
                    url: 
"getJsonHandler.ashx",
                    type: 
'GET',
                    data: {},
                    dataType: 
'json',
                    timeout: 
1000,
                    error: 
function(XMLHttpRequest, textStatus, errorThrown) { alert(textStatus) },
                    success: 
function(result) {
                        alert(result.make);
                        alert(result.model);
                        alert(result.year);
                        alert(result.color);
                    }
                });
            });

C#后台生成代码

public class getJsonHandler: IHttpHandler
    {
public void ProcessRequest(HttpContext context)
        {
            TestObj obj 
= new TestObj();
            obj.make 
= "Make is Value";
            obj.model 
= "Model is Value";
            obj.year 
= 999;
            obj.color 
= "Color is Value";
            context.Response.Write(obj.ToJsJson());
        }
 
public bool IsReusable
        {
            
get
            {
                
return false;
            }
        }
}
//返回值为 {"color":"Color is Value","make":"Make is Value","model":"Model is Value","year":999}

 ——————————————C#由Json生成对象—————————————————

JavaScript调用测试代码

           $('#postJson').click(function() {
                
var m_obj = { make: "Dodge", model: "Coronet R/T", year: 1968, color: "yellow" };
                
var jsonStr = JSON.stringify(m_obj); //用Json2.js生成Json字符串

                $.ajax({
                    url: 
"postJsonHandler.ashx",
                    type: 
'POST',
                    data: { postjson: jsonStr },
                    dataType: 
'json',
                    timeout: 
1000,
                    error: 
function(XMLHttpRequest, textStatus, errorThrown) { alert(textStatus) },
                    success: 
function(result) {
                        alert(result.success);
                    }
                });
});

C#后台生成代码

public class postJsonHandler: IHttpHandler
    {
        
public void ProcessRequest(HttpContext context)
        {
            
string jsonStr = context.Request["postjson"];
            TestObj obj 
= jsonStr.FromJsonTo<TestObj>();
            
if (string.IsNullOrEmpty(obj.make) || string.IsNullOrEmpty(obj.model) || string.IsNullOrEmpty(obj.color) 
|| obj.year < 0)
            {
                context.Response.Write(
"{success:false}");
            }
            
else
            {
                context.Response.Write(
"{success:true}");
            }
public bool IsReusable
        {
            
get
            {
                
return false;
            }
        }
}

 

使用Json时需要注意,服务器端拼凑生成Json字符串时,一定要注意把字符串用\"\"包裹,不然客户端接收时肯定会报错,根据Json字符串生成对象,是根据对应的名称赋值,多于或少于都不会报错.PS:小学语文没学好,让各位见笑.

[C#]ASP.NET2.0国际化/本地化应用程序的实现总结(多语言,多文化页面的实现)

mikel阅读(945)

转载:http://www.cnblogs.com/aierong/archive/2008/11/25/culture.html

最近给单位做了一个业务下单系统,由于在全球几个国家都有业务办事处,所以下单系统做的是多语言界面的
我实现多语言是利用了ASP.NET2.0的资源文件,其实据我所知除了资源文件的方式,在数据库里面建多个表中进行一个区域文化映射,也是解决国际化的一种方式(关于这种方式感兴趣的朋友可以自己试试)
现在我把实现国际化用到的相关知识总结如下:

在总结全球化知识前,先来了解一个名词:表达式语法
表达式语法格式:<%$ … %>
它是ASP.NET2.0新增了一种声明性表达式语法,可在分析页之前将值替换到页中.
ASP.NET表达式是基于运行时计算的信息设置控件属性的一种声明性方式.
ASP.NET表达式主要应用在:连接字符串,应用程序设置,资源文件等地方.

ASP.NET 表达式的基本语法如下:
<%$ expressionPrefix: expressionValue %>

下面演示表达式语法在连接字符串和应用程序设置的应用:

a.表达式语法在连接字符串的应用
将存储在Web.config文件中的连接字符串的值设置为控件的连接字符串属性
<asp:SQLDataSource ID="SQLDataSource1" Runat="server"
    SelectCommand="Select * FROM Employees"
    ConnectionString="<%$ ConnectionStrings:NorthwindCon %>">
</asp:SQLDataSource>

<configuration>
  <connectionStrings>
    <add name="NorthwindCon"
      connectionString="Data Source=yanfa0;Integrated Security=SSPI;Initial Ctalog=Northwind;"
      providerName="System.Data.SqlClient" />
  </connectionStrings>
</configuration>

b.表达式语法在应用程序设置的应用
使用表达式来引用在Web.config配置文件中定义的应用程序设置
<asp:Label ID="Label1" runat="server" Text="<%$ AppSettings:Txt %>"></asp:Label>

<appSettings>
 <add key="Txt" value="Abc"/>
</appSettings>

其实表达式语法还有一个重要的应用,就是在资源文件中的使用,而资源文件正是我实现本地化的基础.

ASP.NET2.0提供了三项关于网站国际化的特性:
(1)自动侦测浏览器的语言
通过确定发出请求的浏览器的语言,可以方便地根据用户的区域性调整应用程序的行为
(2)声明本地化的表达式
声明性资源表达式可使您的应用程序使用多种语言,而不必手动编写代码来检索资源并在页中进行替换
只需使用ASP.NET2.0中新提供的表达式语法即可对资源替换进行定义
(3)生成本地资源
ASP.NET2.0支持标准的resx文件格式自动进行运行时资源检索。
Vs2005也提供了相应的支持,可方便地生成资源

 

A.区域性的格式设置
区域性名称和标识符名称遵循RFC 1766标准,具体请查看msdn
请看下图,我抓取的部分区域性图片:

区域性通常分为三个类型:固定区域性、非特定区域性和特定区域性。
a.固定区域性不区分区域性。可以使用空字符串("")或者按区域性标识符0x007F来指定固定区域性,它与英语语言关联,但不与任何国家/地区关联。
b.非特定区域性是与某种语言关联但不与国家/地区关联的区域性。
c.特定区域性是与某种语言和某个国家/地区关联的区域性。
例如:"fr"(法语)是非特定区域性,而"fr-FR"(法语(法国))是特定区域性
特别提示:"zh-CHS"(简体中文)和"zh-CHT"(繁体中文)是非特定区域性。

区域性是有层次结构的,即特定区域性的父级是非特定区域性,而非特定区域性的父级是InvariantCulture

利用ASP.NET2.0可轻松地逐页更改区域性设置
只需将UICulture和Culture(区域性)属性添加到.aspx等类似页面内的Page指令即可
<%@ Page  Culture="Auto" UICulture="Auto" %>

要想对站点中的所有页面都设置相同的区域性设置,只需将以下元素添加到位于站点的根处的web.config文件中即可,这样就不必分别为每个页面进行分配了
<globalization uiCulture="auto" culture="auto" />

还可以为ASP.NET指定一个默认区域性
以下指定页面的用户界面区域性是自动,默认区域性是英语,页的区域性是自动,默认区域性是英语(美国)
<globalization uiCulture="auto:en" culture="auto:en-US" />

Culture:指示页的区域性设置
UICulture:指定用于页的用户界面(UI)区域性设置

关于Internet Explorer中设置首选语言,可以如下配置:
1.选择“工具 | Internet 选项”命令。
2.选择“语言”按钮。
3.在显示的对话框中,单击“添加”按钮添加一个新的语言并选择一个新的区域性。单击“确定”。
4.请确保首选语言位于列表的顶部。选择该语言并单击“上移”。单击“确定”退出对话框。
5.刷新。

 

B.设置语言首选项
自动检测功能所选择的默认Culture和UICulture有时可能不是用户所需要的
例如:一个美国籍业务员来到中国出差,在中国总部上网下业务单.这种情况下,Web应用程序应该为用户提供显式更改语言的功能,给他提供英文版的业务下单页

using System.Threading;
using System.Globalization;

protected override void InitializeCulture ( )
{
    //显式的指定区域为en,为美国籍业务员显示英文页
    //实际应用中可以根据用户选择来动态设置语言
    //还可以利用Profile来记住用户选择的语言,以便用户以后访问网站时自动进入相应语言页
    Thread.CurrentThread.CurrentUICulture = new  CultureInfo ( "en" );
    Thread.CurrentThread.CurrentCulture =  CultureInfo.CreateSpecificCulture ( "en-US" );
}

注意:对Thread.CurrentThread.CurrentCulture和 Thread.CurrentThread.CurrentUICulture所做的更改需要在InitializeCulture()方法中进行,因为 对首选浏览器语言的自动检测是在页生存期的早期发生的

特别提示:
CultureInfo类的实例化一般有两个途径,如下所示:
CultureInfo culture = CultureInfo. CreateSpecificCulture (name);
CultureInfo culture = new CultureInfo(name);
二者的区分:
使用第一种方法,只能创建固定区域性或特定区域性的CultureInfo实例。
使用第二种方法,则是建立一个name所指定的区域性的CultureInfo实例,它可以是固定的,非特定的或特定区域性的

Thread类的CurrentCulture属性用来获取或配置当前线程的区域性,它必须被配置为特定区域性。
假如Thread.CurrentThread.CurrentCulture = new CultureInfo ( "en" );就会报错!

 

C.全局资源和本地资源
资源文件实际是XML文件,其中包含要转换为不同语言或图像路径的字符串。
资源文件包含键/值对,每一对都是一个单独的资源。键名不区分大小写。
ASP.NET中的资源文件具有.resx扩展名。在运行时,.resx 文件将编译进一个程序集内。

全局资源位于一个名为/App_GlobalResources的专用文件夹中,该文件夹位于应用程序的根目录下。
所有页、用户控件等都可访问这些资源,因此全局资源通常用作共享资源。
资源文件的名称用在显式表达式中,但也采用namespace.classname形式,以便在应用程序的Resources命名空间下进行强类型访问。

本地资源在同级/App_LocalResources文件夹下定义,RESX 文件遵循关联页、用户控件或母版页以及区域性定义的命名约定.
例如:与Index.aspx页一起有一个名为App_LocalResources的文件夹,本地资源就放在该文件夹中,该文件夹根据页的名称相应命名如下:
Index.aspx.resx。非区域性特定资源文件
Index.aspx.fr.resx。区域性特定资源文件(法语)
Index.aspx.de.resx。区域性特定资源文件(德语)
文件的基名称与页文件名相同,后跟语言和区域性名称,最后以扩展名 .resx 结尾

提示:Vs2005可以在设计视图编辑器中打开页、用户控件或母版页时,在工具菜单中提供一个名为生成本地资源的命令。该命令可自动创建默认区域性的本地资源文件。
若要创建其他语言的资源文件,可以在解决方案资源管理器中复制该文件,然后按照以下模式之一重命名它:
对于全局资源文件:
name.language.resx
name.language-culture.resx
对于局部资源文件:
pageOrControlName.extension.language.resx
pageOrControlName.extension.language-culture.resx
例如,如果创建名为WebResources.resx的全局资源文件,且该文件将翻译为英语,则请将复制的文件命名为WebResources.en.resx。请注意,对于局部资源文件,资源文件名是页或控件的名称(包括文件扩展名),后面是语言和区域性信息。

全局资源文件和本地资源文件之间选择?
通常情况下,希望在各页之间共享资源时,应向全局资源文件添加这些资源。
全局资源文件中的资源还是强类型的,用于以编程方式访问。
但如果将所有本地化资源都存储在全局资源文件中,则这些文件会变得很大。
此外,如果多个开发人员要处理不同的页但在同一个资源文件中工作时,全局资源文件也会更难于管理。
本地资源文件使得单个ASP.NET网页的资源比较容易管理,但不能在各页之间共享资源。
此外,如果有许多页必须本地化为多种语言,则可能会创建大量本地资源文件。

提示:
在对默认资源文件进行更改时,无论它是本地资源文件还是全局资源文件,ASP.NET都重新编译资源并重新启动ASP.NET应用程序
在添加附属资源文件将不会导致重新编译资源,但ASP.NET应用程序将重新启动

 

D.资源表达式
有两种形式的资源表达式:显式和隐式

(1).本地资源的隐式表达式
如果已为页创建了本地资源文件,则可以使用隐式本地化从该资源文件中为控件填充属性值。
使用隐式本地化时,ASP.NET读取资源文件并将资源与属性值相匹配。
在页上,对该控件的标记使用特殊的 meta 属性可指定隐式本地化.

<asp:Button ID="Button1" runat="server" Text="Button" meta:resourcekey="Button1Resource1" />

resourcekey值与相应资源文件中的键相匹配,如果在资源文件中定义了某个属性值,则ASP.NET会用资源值替换该属性。

(2).本地资源的显式表达式
与隐式本地化不同,必须为要设置的每个属性分别使用一个资源表达式。
该表达式采用以下形式,ResourceID是必需的:
<%$ Resources:  ResourceID %>

<asp:Button ID="Button1" runat="server"
 Text="<%$ Resources: Button1Resource1.Text  %>"
    ToolTip="<%$ Resources:Button1Resource1.ToolTip %>" />
               

(3).全局资源的显式表达式
该表达式采用以下形式:
<%$ Resources: Class , ResourceID %>
Class是全局资源时使用的资源文件,ResourceID是要读取的资源的标识符.

<asp:Button ID="Button1" runat="server"
    Text="<%$ Resources : ResourceGlobal, Button1Txt  %>"
    ToolTip="<%$ Resources : ResourceGlobal, Button1ToolTip  %>" />

 

E.对全局资源和本地资源的编程访问
调用GetLocalResourceObject或GetGlobalResourceObject方法以分别从全局资源文件或本地资源文件中读取特定的资源

//访问本地资源
Button1.Text = GetLocalResourceObject ( "Button1Resource1.Text" ).ToString ( );
Button1.ToolTip = GetLocalResourceObject ( "Button1Resource1.ToolTip" ).ToString ( );
//访问全局资源
Button1.Text = GetGlobalResourceObject ( "ResourceGlobal" , "Button1Txt" ).ToString ( );
Button1.ToolTip = GetGlobalResourceObject ( "ResourceGlobal" , "Button1ToolTip" ).ToString ( );

 

F.编码
对于比较旧的浏览器以及移动设备上的浏览器,可能有必要将与浏览器之间的通信转换成某种基于代码页的旧式编码
在web.config文件的globalization节中配置即可

 

G.代码下载
http://files.cnblogs.com/aierong/WebSite_CultureInfo.rar

 

作者:http://www.cnblogs.com/aierong

[MVC]ASP.NET MVC: Using UserControls Usefully

mikel阅读(791)

转载:http://blog.wekeroad.com/2008/01/07/aspnet-mvc-using-usercontrols-usefully/

This is an interesting issue, and as he went on to point out, there are lots of ways to use these partial UI bits to your advantage. As always, I'll offer some ideas here for you, but this is not the only way to do it.

 

The "Viewlet"
Given that a ViewPage inherits from System.Web.Page, you can still register and reference a UserControl (.ascx file) on a View and have it render. You can even pass it data in the code behind or as a property setting:

<uc1:MyUserControl ID=myControlView runat=server myProperty="Hi Mom" />

But using the WebForms model inside MVC can be a little confusing – especially for folks who will pick up your project later. In this case – the ViewUserControl will render just fine, and in many cases it's all you need. But if you're interesting in keeping things as modular as possible within MVC, read on.

Your UseControl can be one of two things:

  • A granular bit of UI that renders information passed from a Controller
  • A granular bit of UI that renders information from an application-wide data source

The idea here is reusability and maintainability.

I personally don't like the idea of putting logic into ViewUserControls, but at the same time it can serve a purpose – with portal stats for instance where you might want to serve up personalized links (using the Memebership.Profile for instance), etc.

In other MVC frameworks they make a distinction between the two. In Rails, for instance, a "Partial" is simply a View that's shared between views, and not meant to be standalone. If you've ever used Rails to create the demo scaffold, you've used the partial "_form.rhtml" which is responsible for rendering an input form for the New and Edit views.

If you need more than basic rendering of HTML in Rails, you can move to a Layout. Layouts essentially wrap UI around some logic, and are a nice modular way to reuse UI/logic elements around an application. These are like "UserControls Light".

Rails also allows for "sub apps" called "Components", which are useful when you have "sub applications" like an image gallery or rss reader. The idea here is that these components contain their own logic and can be used between many applications.

My point with all this is that if you feel lost trying to make a decision (architecturally) regarding MVC – it helps to see how other platforms do it (like Rails or Django).

 

Rendering a ViewUserControl
The MVC Toolkit has a nice method called "RenderUserControl()" that allows you to process your ViewUserControl and output it's result inline:

 
<%=Html.RenderUserControl(“~/UserControls/UserList.ascx”)%>

If the ViewUserControl is typed (say ViewUserControl<MyControllerData>), then it's ViewData object will be filled for you, and your ViewPage and rendered control will share the same data.

If you want to be explicit about it, you can do that as well, specifying the data to pass:


<%=Html.RenderUserControl(“~/UserControls/UserList.ascx”,ViewData.Users)%>

Finally, if you need to set properties on the ViewUserControl, you can do that as well by passing in an anonymous type:


<%=Html.RenderUserControl(“~/UserControls/UserList.ascx”,ViewData.Users, new {GroupID=2})%>

 

Using the RenderUserControl method allows you to have complete control over how your ViewUserControl is supposed to be used

 

Summary
UserControls are a great way (still) to encapsulate UI elements for your MVC app. There are many ways to use them, and if you have one I didn't mention (or dislike my approach here) – do let me know. As always – we're still CTP and there's lots of room for comments.

[MVC]ASP.NET MVC Tip #14 – 创建模板辅助方法

mikel阅读(863)

[翻译] ASP.NET MVC Tip #14 – 创建模板辅助方法

摘要:在这个Tip中,你将学到在MVC框架中显示数据库数据时,如何创建和使用模板。Stephen Walther介绍了如何创建一个名为RenderTemplate()的辅助方法。

7月4号这个周末,当我回到加利福尼亚的家中后,我和我聪明的哥哥谈起来使用ASP.NET Web Forms、ASP.NET MVC和Ruby on Rails创建Web应用程序的不同。我于是感叹在创建ASP.NET MVC应用程序时,我真的很想念控件。我尤其想念ASP.NET Web Forms控件中的模板带来的HTML与UI逻辑之间的清晰的分离。Repeater控件和for…next循环真的不一样。

我的哥哥告诉我一个很吃惊的东西。他说“模板,Ruby on Rails有模板,他们称之为partials。”最初,我并不理解。我一位Ruby on Rails中的partials或多或少和ASP.NET MVC中的用户控件有点像。然而,我哥哥向我解释了当在Ruby on Rails应用程序中呈现一个partial时,你可以向其传递一组项的集合。集合中的每一项都由partial来呈现。

酷。你可以用同样的方式在ASP.NET MVC应用程序中创建模板。创建一个新的辅助方法,它接受一个IEnumerable和一个用户控件的路径。对于IEnumerable中的每一个元素, 辅助方法都会将用户控件作为一个模板。清单1包含了名为RenderTemplate()的辅助方法。

清单1 – TemplateExtensions.cs

假设你想显示一个电影列表。你可以使用清单2中的HomeController返回一个电影实体的集合。Index()操作执行了一个LINQ to SQL查询,并将查询结果传递给Index视图。

清单2 – HomeController.cs

清单3中的视图简单地调用了RenderTemplate()方法,并肩ViewData.Model和一个包含了每一个电影模板的MVC用户控件的路径传递给该方法。

清单3 – Index.aspx

MovieTemplate.ascx MVC用户控件是强类型的。清单4列出了该用户控件的后台代码。注意用户控件的强类型,使其只能展现Movie实体。

清单4 – MovieTemplate.ascx.cs

最后,清单5给出了MVC用户控件的视图部分。注意你可以使用ViewData.Model.Title和 ViewData.Model.Director这样的表达式来显示电影的标题和导演。这些表达式能够工作是因为你为MVC用户控件使用了强类型,使它只 能展现电影实体。

清单5 – MovieTemplate.ascx

当你请求Index视图时,你将得到图1所示的页面。注意对于每个电影都呈现了一个MVC用户控件。

图1 – 使用模板呈现电影记录

图1 - 使用模板呈现电影记录

小结

在这个Tip中,我介绍了在ASP.NET MVC应用程序中如何创建和使用模板。我演示了如何通过创建MVC用户控件来创建模板,以及如何使用模板来呈现一组数据库记录。今后再也不用在ASP.NET MVC应用程序里惦记着使用Repeater控件了。

此处下载源代码:http://weblogs.asp.net/blogs/stephenwalther/Downloads/Tip14/Tip14.zip

[C#]CLR系列:浅析泛型的本质

mikel阅读(946)

转载:http://www.cnblogs.com/gjcn/archive/2008/11/25/1338407.html

泛型早在.net2.0就出来了,大家已经对它的使用很熟悉了,在工作中也大量的使用过它,但是大家对其工作原理,以及MS针对泛型对CLR做了那些工作是否了解呢。本文就是对泛型的本质进行讲解,希望能对大家有所帮助。

1 引入泛型

.Net2.0出来的时候,大家很轰动,其中.NET2.0做的一个很大的改变就是增加了泛型。在1.1的版本,大家还在使用一些如:ArrayList等集合。就算现在是.net3.5的时代,还是有很多程序员在继续使用1.1版本的集合,并没有采用范型集合,毕竟一个新技术的使用是要一段时间的,也许大家看了本文后会在适当的时候使用范型。

1 ArrayList arr = new ArrayList();
2 List<int> arr1 = new List<int>();
3 for (int i = 1; i < 10; i++)
4 {
5     arr.Add(i);
6     arr1.Add(i);
7 }

这是一个很基础的泛型应用,可能大家大部分使用泛型都是类似上面的方式。我们就从这个简单的代码讲起。表面上看好像1.1版本和2.0版本的集合使用上没什么其别,一样的方便。但是实质上MS在底层做了很多复杂的工作。我们看看它的IL代码:

1 IL_0012:  ldloc.0
2 IL_0013:  ldloc.2
3 IL_0014:  box        [mscorlib]System.Int32
4 IL_0019:  callvirt   instance int32 [mscorlib]System.Collections.ArrayList::Add(object)
5 IL_001e:  pop
6 IL_001f:  ldloc.1
7 IL_0020:  ldloc.2
8 IL_0021:  callvirt   instance void class [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0)
9 

在这里我们看看它第三行,熟悉IL的人都知道。这行box是在执行装箱操作。我们可以看看的Add方法原型:

Code

我们可以看到ArrayList的Add方法的参数是object类型,我们都知道int --> object是要经过装箱操作,装箱操作又是个很费时间的事情,这就影响了性能。这里我们就引出泛型的第一个好处:性能的好处,避免了频繁的装箱拆箱操 作。泛型的第二个好处:保证了类型的绝对安全,这点就不多讲了。上面的IL里多了一个类 型:System.Collections.Generic.List`1这是.net CLR为泛型生成一个带“'”的类型,后面的数字表示<x,x>里参数x的个数。还有一个就是!0,这个是什么呢,请看下面的分析。

2 解析泛型

还是先看看一段代码:

public void intrGeneric<T>()
{
   Console.WriteLine(
typeof(T));
}
intrGeneric
<int>();
intrGeneric
<object>();
intrGeneric
<string>();

那么当我们不知道intrGeneric<T>的T的类型的时候,IL是在怎么处理的呢。看看产生的IL:

 1 .method public hidebysig instance void  intrGeneric<T>() cil managed noinlining
 2 {
 3   // Code size       18 (0x12)
 4   .maxstack  8
 5   IL_0000:  nop
 6   IL_0001:  ldtoken    !!T
 7   IL_0006:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
 8   IL_000b:  call       void [mscorlib]System.Console::WriteLine(object)
 9   IL_0010:  nop
10   IL_0011:  ret
11 // end of method Generics_CSharp::intrGeneric

 在这里我们可以看到编译器产生的IL是并没有为T指定一个特定的类型,但是输出的结果为System.Int32,System.Object,System.String。我们知道typeof()输出的Runtime Type。我们很容易的得出泛型的另外一个特性:运行时本质。由CLR运行时支持,真正的泛型实例化是发生在JIT编译时,生成不同的本地代码。我们再来看看上面的IL代码:ldtoken    !!T,ldtoken 指令:将元数据标记转换为其运行时表示形式,并将其推送到计算堆栈上。!!T时编译器生成的一个占位符,工作形式时这样的:第一次编译的时候,首先生成 IL代码以及元数据,T只是一个隐藏的符号,这是并没有对泛型类型进行实例化,当JIT编译的时候,将以实际类型替换IL代码和元数据的T符号,并将其转 换为本地代码。

 说到这里我们大概对泛型有个大概的了解。Java编译器在编译泛型的时候,会将所有的 泛型参数替换为Object,这实际上还是存在装箱拆箱的过程。还有C++的实现也存在一个很大的问题,那就是代码爆炸,C++会为每种类型都要生成自己 的一份代码。C#与JAVA比较解决了性能问题,与C++比较,解决了代码爆炸的问题。那么C#是怎么解决代码爆炸的问题,做到代码共享的呢?这也是C# 泛型的一个特性。

我们知道一个程序域有个loader heap,里面有个method table,每种类型都有自己的一个一个方法表,而且这个方法表列出所有的方法,方法表中包含了一个槽表,指向各个方法的描述MethodDesc), 提供了类型的行为能力,用于不同的交互操作实现的调用,在第一次调用时,会调用JIT编译程序,对其进行扫描,发现有引用别的类型,就会导向元数据,让后 再根据元数据导向到所要查找的类型的具体位置,当范型参数是引用类型的时候,引用类型变量都是指向托管堆的指针,(在32位的操作系统上指针都是32位 的)而对于指针完全是可以用相同的方式操作的。这再很大程度上避免了代码爆炸。如果为值类型的时候,由于值类型都是直接操作数据本身,JIT不会为一个不 知道大小的参数去产生同样的代码。下次JIT编译的时候,先去查找是否有相同的类型代码,如果有的话,就不会再次编译。因此C#泛型做到了时间和空间的双 重效应。看看下面windbg+sos的调试,我们看看它经过JIT编译过后的本地代码,我们更加肯定的验证了以上的我们所论述的。

 1 0:010> !u 00dc1128
 2 Normal JIT generated code
 3 Collections.Program.Main(System.String[])
 4 Begin 00dc1128, size 90
35 00dc117f 8bdf            mov     ebx,edi
36 00dc1181 8bce            mov     ecx,esi
37 00dc1183 3909            cmp     dword ptr [ecx],ecx
38 00dc1185 e886b4cbff      call    00a7c610 (Collections.Generics_CSharp.intrGeneric[[System.Int32, mscorlib]](), mdToken: 06000003)
39 00dc118a 90              nop
40 00dc118b 8bce            mov     ecx,esi
41 00dc118d ba2892a700      mov     edx,0A79228h (MD: Collections.Generics_CSharp.intrGeneric[[System.Object, mscorlib]]())
42 00dc1192 3909            cmp     dword ptr [ecx],ecx
43 00dc1194 e897b4cbff      call    00a7c630 (Collections.Generics_CSharp.intrGeneric[[System.__Canon, mscorlib]](), mdToken: 06000003)
44 00dc1199 90              nop
45 00dc119a 8bce            mov     ecx,esi
46 00dc119c ba6892a700      mov     edx,0A79268h (MD: Collections.Generics_CSharp.intrGeneric[[System.String, mscorlib]]())
47 00dc11a1 3909            cmp     dword ptr [ecx],ecx
48 00dc11a3 e888b4cbff      call    00a7c630 (Collections.Generics_CSharp.intrGeneric[[System.__Canon, mscorlib]](), mdToken: 06000003)
49 00dc11a8 90              nop
50 00dc11a9 e81e886278      call    mscorlib_ni+0x3299cc (793e99cc) (System.Console.Read(), mdToken: 060007b6)
51 00dc11ae 90              nop
52 00dc11af 90              nop
53 00dc11b0 8d65f4          lea     esp,[ebp-0Ch]
54 00dc11b3 5b              pop     ebx
55 00dc11b4 5e              pop     esi
56 00dc11b5 5f              pop     edi
57 00dc11b6 5d              pop     ebp
58 00dc11b7 c3              ret
59 

这里调试的正式调用泛型方法的函数,看看红色的地方,看看这三个CALL,大家可以看到第一个(int)的地址是:00a7c610 ,后面两个(object和string)的地址都是:00a7c630 。 这可以说明值类型是不同的类型生成不同的代码,而引用类型是共用一个代码。大家还可以看到mdToken: 06000003。这也说明无论是值类型还是 引用类型都是共用一个占位符,引用的是同一个类型下的方法和同一个MethodDesc,进而验证泛型是基于JIT的。

由此我们可以看出为了是泛型能够工作,MS必须做一下的工作:

创建新的IL指令,使之能够识别类型参数。

修改现有的元数据表的格式。

修改各种编程语言使他们能支持泛型。

修改编译器,能生成新的IL和元数据。

修改JIT编译器。

 以上是本人对C#泛型的一点理解。。关于泛型的特性和语法不是本文的重点,园子里有很多文章可以参考。有不同意见欢迎大家指出来。希望大家看了本文后对泛型有个更加深入的理解。

 

待续。。。

[JavaScript]Javascript版飞行射击游戏《天机》1.0正式版发布(附源代码)

mikel阅读(668)

转载:http://www.cnblogs.com/Random/archive/2008/11/24/1339648.html
《天机》的1.0正式版终于可以发布了,从0.6版到1.0版的发布,经历了3个多月近4个月的时间。倒不是因为开发了那么长时间,而是这段时间又经历了 很多事,每个时间段总是有事情要处理,是在时间间隙中开发,再加上个人在后段的开发过程中投入的精力没有之前那么多了,所以进度就稍慢了一些。
      1.0版是我发布的一个正式版,也很有可能是最终版,因为后期可能会放下这个作品,做其它想法的尝试了,而且所有代码我会也在这次发布的时候全部公布出 来。这次做了一点点优化,因为我给游戏中的每个敌人都是加了一个是否被字弹击中的侦听,之前的做法是,一个关卡的所有敌人,从一开始全部都加上侦听,这样 性能就有点低。现在的做法就是在敌人出场的时候再给它加上侦听,这样就减少了同时侦听判断数,效率上就相对高一些了。不过JS的游戏确实在图形表现上性能 还是不算太高,所以一但画面中图形数量多的话,运行速度就会明显变慢了,所以还是推荐大家用配置好一点的机器来进行测试。
      浏览器方面我尽量做到了最大兼容,目前在IE6、IE7、FF3、Opera、Safari、Chrome上都能比较好地运行,不过个人感觉在FF3和Chrome上的效果比较好,大家也可以在多个浏览器上跑一下试试。
      ok,现在把游戏的说明简单给大家介绍一下:

        操作菜单

       

 

        1 – 开始游戏
        2 – 键位设置,可以根据个人的喜好设置相应的操作按键,默认是:W-上、S-下、A-左、D-右、J-发射、K-炸弹。
        3 – 难度设置,有三个难度,如果你觉得默认的简单难度没挑战,可以选择高一点的难度,肯定不会让你失望。
        4 – 语言设置,目前支持简单中文和英文。
        5 – 关于作者
      关卡
        一共有四个关卡,关卡的顺序是随机排列的,也就是说,每次玩的时候,关卡的出现顺序都不一样,关卡越到后面,难度越大。
      金牌奖励
        游戏中击爆某些敌人,会有金牌徽章的奖励,而金牌会旋转出几种角度,每个角度的分值都不一样,玩家的飞机获取的分值由获取金牌时的角度来决定,以下是各个角度的分值:

        500分

        1000分

        2000分

          4000分

 

      增加生命
        游戏中我加入了增加生命的功能,当玩家分数达到以下几种情况的时候,会增加一条生命:
        50000,100000,150000,200000,250000,300000
      蓄力

        长按发射键大约3秒左右,会让玩家的飞机蓄力,松开发射键就会发射蓄力的子弹。

 

      以上是简单的一些介绍,还有一些功能有兴趣的同学可以慢慢体会。下面给出游戏的所有的代码和相关图片及运行地址,因为本人水平有限,所以代码可能有很多地方写得不够好,希望大家多多提意见,谢谢。

       

 

        我是源代码+图片

 

 

 

申明:该游戏的99%图像取材皆来自于“彩京”几个版本的飞行射击游戏,其版权归彩京公司所有。

   另:再次感谢小何提供的空间赞助(虽然你现在不知道我又上传了N多东西……HOHO~~~)

相关链接:天机0.6版及相关说明     天机开发手记

[JQuery]微软将 jQuery IntelliSense整合到Visual Studio

mikel阅读(794)

上个月,我在博客里宣布了微软将对JQuery提供支持。在过去的几个星期里,我们与JQuery开发团队合作,在Studio 2008 和 Visual Web Developer 2008 Express版本(免费的)中增加了很好的jQuery intellisense支持。现在这个支持可以下载使用了。
在VS 2008中启用jQuery Intellisense的步骤
要在VS中启用jQuery的intellisense完成,你要遵循三个步骤:
第一步: 安装VS 2008 SP1
VS 2008 SP1 在Visual Studio中加了更丰富的JavaScript intellisense支持,对很大部分的JavaScript库加了代码完成支持。
你可以在这里下载VS 2008 SP1 和 Visual Web Developer 2008 Express SP1。
第二步: 安装VS 2008 Patch KB958502以支持"-vsdoc.js"Intellisense文件
2 个星期前,我们发布了一个补丁,你可以将其运用到VS 2008 SP1 和 VWD 2008 Express SP1版本上,该补丁会导致Visual Studio在一个JavaScript库被引用时,查找是否存在一个可选的"-vsdoc.js"文件,如果存在的话,就用它来驱动 JavaScript intellisense引擎。
这些加了注释的"-vsdoc.js"文件可以包含对JavaScript方法提供了帮助文档的XML注释,以及对无法自动推断出的动态JavaScript签名的另外的代码intellisense提示。你可以在这里了解该补丁的详情。你可以在这里免费下载该补丁。
第三步: 下载jQuery-vsdoc.js文件
我们与jQuery开发团队合作编写了一个jQuery-vsdoc.js文件,该文件对串连的jQuery selector方法的JavaScript intellisense提供了帮助注释和支持。你可以在jQuery.com网站的官方下载网页上下载jQuery和jQuery-vsdoc文件:
把jquery-vsdoc.js保存到你项目中jquery.js文件所在的同一个目录中(同时确认它的命名前缀与jquery文件名匹配):
然后你可以象这样,通过一个html <script/>元素来引用标准的jquery文件:
或者也可以使用<asp:scriptmanager/> 控件来引用它,或者在一个单独的.js文件的顶部加 /// <reference/> 注释来引用它:
在完成之后,VS就会在你引用的脚本文件所在的同一个目录中寻找一个-vsdoc.js文件,如果找到的话,就会用它来做帮助和intellisense。
例如,我们可以使用jQuery来做一个基于JSON的get请求,得到该方法的intellisense(挂在$.之后):
以及 $.getJSON()方法参数的帮助/intellisense:
如果你在方法调用中嵌套回调函数的话,intellisense依旧会工作。例如,我们也许想对从服务器返回的每个JSON对象进行迭代:
对每个项,我们可以执行另一个嵌套的回调函数:
我们可以使用each回调函数动态地往列表中附加一个新图片(图片的src属性将指向返回的JSON媒体图片的URL):
然后在每个动态生成的图片上,我们可以连接一个点击事件处理函数,在点击时,会通过动画效果来消失:
注意jQuery intellisense在我们代码的每一个层次都很干净地做了提示。
JavaScript Intellisense 技巧和诀窍
Web工具开发团队的Jeff King本星期早先时候撰写了一个很棒的贴子,对有关VS 2008中JavaScript intellisense工作原理的若干常见的问题做了回答,我高度推荐阅读该文。
他 谈到的一个诀窍(我要在这里做示范)是在你想要在用户控件/部分(.ascx文件)中使用JavaScript intellisense时可以使用的一个技术。经常地,你不想要在这些文件中包括对JavaScript库的<script src=""/> 引用,这些引用往往是存在于使用了用户控件的母版页或内容网页之上的。当然,问题是,你这么做的话,在默认情形下VS是无法知道用户控件中用到了这个脚 本,因此不会为你提供intellisense 。
启用intellisense的一个方法是,在你的用户控件中加<script src=""/>元素,但在其周围加一个服务器端的<% if %> 块,在运行时其值总是为false:
在运行时,ASP.NET不会显示这个脚本标识(因为是包含在一个总是为false的if块中的),但是,VS却会运算这个<script/>标识,在用户控件中为它提供intellisense。在象用户控件这样的场景下,这是个非常有用的技术。Jeff在他的FAQ贴子和原先的jQuery intellisense贴子里还有更多细节。Rick Strahl在这里也有一篇很好的贴子,是关于使用jQuery intellisense的。
更多信息
想进一步了解jQuery的话,我建议观看Stephen Walther在PDC大会上做的《ASP.NET和jQuery》讲座。点击这里下载他的代码例程和 powerpoint讲义。
Rick Strahl也有一篇非常棒的《Introduction to jQuery》文章,讨论如何在 ASP.NET使用jQuery。Karl Seguin 在这里和这里有2篇非常好的jQuery基础教程贴子,对如何使用jQuery的一些基本知识提供了比较简短的的概述。
我也高度推荐《 jQuery in Action》一书。
希望本文对你有所帮助,
Scott
翻译:Scott Guthrie 博客中文版

[C#]方法的直接调用,反射调用与……Lambda表达式调用

mikel阅读(927)

转载:http://www.cnblogs.com/JeffreyZhao/archive/2008/11/22/invoke-method-by-lambda-expression.html
想调用一个方法很容易,直接代码调用就行,这人人都会。其次呢,还可以使用反射。不过通过反射调用的性能会远远低于直接调用——至少从绝对时间上来 看的确是这样。虽然这是个众所周知的现象,我们还是来写个程序来验证一下。比如我们现在新建一个Console应用程序,编写一个最简单的Call方法。

class Program
{
static void Main(string[] args)
{
}
public void Call(object o1, object o2, object o3) { }
}

  Call方法接受三个object参数却没有任何实现,这样我们就可以让测试专注于方法调用,而并非方法实现本身。于是我们开始编写测试代码,比较一下方法的直接调用与反射调用的性能差距:

static void Main(string[] args)
{
int times = 1000000;
Program program = new Program();
object[] parameters = new object[] { new object(), new object(), new object() };
program.Call(null, null, null); // force JIT-compile
Stopwatch watch1 = new Stopwatch();
watch1.Start();
for (int i = 0; i < times; i++)
{
program.Call(parameters[0], parameters[1], parameters[2]);
}
watch1.Stop();
Console.WriteLine(watch1.Elapsed + " (Directly invoke)");
MethodInfo methodInfo = typeof(Program).GetMethod("Call");
Stopwatch watch2 = new Stopwatch();
watch2.Start();
for (int i = 0; i < times; i++)
{
methodInfo.Invoke(program, parameters);
}
watch2.Stop();
Console.WriteLine(watch2.Elapsed + " (Reflection invoke)");
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}

  执行结果如下:

00:00:00.0135323 (Directly invoke)
00:00:05.2325120 (Reflection invoke)
Press any key to continue...

  通过各调用一百万次所花时间来看,两者在性能上具有数量级的差距。因此,很多框架在必须利用到反射的场景中,都会设法使用一些较高级的替代方案 来改善性能。例如,使用CodeDom生成代码并动态编译,或者使用Emit来直接编写IL。不过自从.NET 3.5发布了Expression相关的新特性,我们在以上的情况下又有了更方便并直观的解决方案。

  了解Expression相关特性的朋友可能知 道,System.Linq.Expressions.Expression<TDelegate>类型的对象在调用了它了Compile方 法之后将得到一个TDelegate类型的委托对象,而调用一个委托对象与直接调用一个方法的性能开销相差无几。那么对于上面的情况,我们又该得到什么样 的Delegate对象呢?为了使解决方案足够通用,我们必须将各种签名的方法统一至同样的委托类型中,如下:

public Func<object, object[], object> GetVoidDelegate()
{
Expression<Action<object, object[]>> exp = (instance, parameters) =>
((Program)instance).Call(parameters[0], parameters[1], parameters[2]);
Action<object, object[]> action = exp.Compile();
return (instance, parameters) =>
{
action(action, parameters);
return null;
};
}

  如上,我们就得到了一个Func<object, object[], object>类型的委托,这意味它接受一个object类型与object[]类型的参数,以及返回一个object类型的结果——等等,朋友们 有没有发现,这个签名与MethodInfo类型的Invoke方法完全一致?不过可喜可贺的是,我们现在调用这个委托的性能远高于通过反射来调用了。那 么对于有返回值的方法呢?那构造一个委托对象就更方便了:

public int Call(object o1, object o2) { return 0; }
public Func<object, object[], object> GetDelegate()
{
Expression<Func<object, object[], object>> exp = (instance, parameters) =>
((Program)instance).Call(parameters[0], parameters[1]);
return exp.Compile();
}

  至此,我想朋友们也已经能够轻松得出调用静态方法的委托构造方式了。可见,这个解决方案的关键在于构造一个合适的Expression<TDelegate>,那么我们现在就来编写一个DynamicExecuter类来作为一个较为完整的解决方案:

public class DynamicExecutor
{
private Func<object, object[], object> m_execute;
public DynamicExecutor(MethodInfo methodInfo)
{
this.m_execute = this.GetExecuteDelegate(methodInfo);
}
public object Execute(object instance, object[] parameters)
{
return this.m_execute(instance, parameters);
}
private Func<object, object[], object> GetExecuteDelegate(MethodInfo methodInfo)
{
// parameters to execute
ParameterExpression instanceParameter = Expression.Parameter(typeof(object), "instance");
ParameterExpression parametersParameter = Expression.Parameter(typeof(object[]), "parameters");
// build parameter list
List<Expression> parameterExpressions = new List<Expression>();
ParameterInfo[] paramInfos = methodInfo.GetParameters();
for (int i = 0; i < paramInfos.Length; i++)
{
// (Ti)parameters[i]
BinaryExpression valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i));
UnaryExpression valueCast = Expression.Convert(valueObj, paramInfos[i].ParameterType);
parameterExpressions.Add(valueCast);
}
// non-instance for static method, or ((TInstance)instance)
Expression instanceCast = methodInfo.IsStatic ? null :
Expression.Convert(instanceParameter, methodInfo.ReflectedType);
// static invoke or ((TInstance)instance).Method
MethodCallExpression methodCall = Expression.Call(instanceCast, methodInfo, parameterExpressions);
// ((TInstance)instance).Method((T0)parameters[0], (T1)parameters[1], ...)
if (methodCall.Type == typeof(void))
{
Expression<Action<object, object[]>> lambda = Expression.Lambda<Action<object, object[]>>(
methodCall, instanceParameter, parametersParameter);
Action<object, object[]> execute = lambda.Compile();
return (instance, parameters) =>
{
execute(instance, parameters);
return null;
};
}
else
{
UnaryExpression castMethodCall = Expression.Convert(methodCall, typeof(object));
Expression<Func<object, object[], object>> lambda = Expression.Lambda<Func<object, object[], object>>(
castMethodCall, instanceParameter, parametersParameter);
return lambda.Compile();
}
}
}

  DynamicExecutor的关键就在于GetExecuteDelegate方法中构造Expression Tree的逻辑。如果您对于一个Expression Tree的结构不太了解的话,不妨尝试一下使用Expression Tree Visualizer来 对一个现成的Expression Tree进行观察和分析。我们将一个MethodInfo对象传入DynamicExecutor的构造函数之后,就能将各组不同的实例对象和参数对象数 组传入Execute进行执行。这一切就像使用反射来进行调用一般,不过它的性能就有了明显的提高。例如我们添加更多的测试代码:

DynamicExecutor executor = new DynamicExecutor(methodInfo);
Stopwatch watch3 = new Stopwatch();
watch3.Start();
for (int i = 0; i < times; i++)
{
executor.Execute(program, parameters);
}
watch3.Stop();
Console.WriteLine(watch3.Elapsed + " (Dynamic executor)");

  现在的执行结果则是:

00:00:00.0148828 (Directly invoke)
00:00:05.2488540 (Reflection invoke)
00:00:00.0618695 (Dynamic executor)
Press any key to continue...

  事实上,Expression<TDelegate>类型的Compile方法正是使用Emit来生成委托对象。不过现在我们已经 无需将目光放在更低端的IL上,只要使用高端的API来进行Expression Tree的构造,这无疑是一种进步。不过这种方法也有一定局限性,例如我们只能对公有方法进行调用,并且除了方法外的其他类型成员,我们就无法如上例般惬 意地编写代码了。

[C#]利用虚拟方法和反射简化Alisoft API的调用

mikel阅读(937)

利用虚拟方法和反射简化Alisoft API的调用

转载:http://www.cnblogs.com/doublog/archive/2008/11/21/1338065.html

最近一直在研究Alisfot api,他的文档和帮助都让我很郁闷,可能是第一次做这种开放api的程序吧.为了简化那些烦人的参数和返回值的调用,可以利用.net的匿名委托和反射机制来达到目的,简化后代码可以如下所示:

/// <summary>

/// 此接口方法以实现得到前台展示的店铺内卖家自定义商品类目。

/// </summary>

/// <param name="Nick"></param>

/// <returns></returns>

public List<SellerCat> sellercats_list_get(string Nick)

{

SellerCatsPar parSL = new SellerCatsPar();

parSL.sip_sessionid = sip_sessionid;

parSL.nick = Nick;

return APIUtils.Result<List<SellerCat>>(parSL, (string result) =>

{

NameValueCollection par = new NameValueCollection();

par.Add("ID", "cid");

par.Add("Name", "name");

par.Add("ParentID", "parent_cid");

return new XmlHandler<SellerCat>().ListItemByXml(result, "rsp/seller_cat", par);

});

}

现在是不是简单明了,只要关注于业务就可以啦.

   

第一次用office发文章,太晚了先发一点内容测试测试.:-)

据说 还能贴图 试试

图一

图一为阿里的API文档菜单,我看了一星期,硬没找到他的api列表(中间一直在看百度搜索到的一个老界面).今天才搞明白,原来我一直在 点右边红框的文字部分,他的内容竟然也变,但是树形菜单不会下拉.只有再点左边的图片才可以.也可以说是我智商的问题,但这里面也有一个用户体验的问题. 在做系统的时候会习惯的认为用户会按照自己的思维做事情,往往一个简单的东西,不同的人就会得到不同的结果.比如这个问题.如果不是因为我继续关注,阿里 的这个api我肯定会以为他没提供而放弃找其他的啦.

[Flex]Ensemble Visual Studio 的Flex插件

mikel阅读(1005)

转载自:http://www.asflex.cn/?p=589

今天在Forbes.com上看到《Ensemble Introduces Tofino, A Visual Studio Plug-In for Flex Applications

翻译一下吧。

Flex 是一个用于开发和维护Web程序(支持大部分的浏览器,桌面和操作系统)的免费开源框架。目前,大部分的Flex开发者都使用基于Eclipse(TM) 的Adobe(R) Flex(R) Builder(TM),用来开发、调试、部署RIA程序。为了让开发者使用Visual Studio,Ensemble Tofino提供了一个.Net的解决方案,让在.Net开发环境中开发Flex成为了可能。

“我们相信Ensemble Tofino将会帮助.Net开发者,因为使用相同的开发界面而不需要手动地调用Flex编译器,从而非常容易地将Flex的优点和.Net服务器语言联合起来。”,Ensemble 的主要技术部门负责人Ray Blaak说。

Tofino提供了一个 强劲的智能化编码和调试功能,同时可以通过Visual Studio相当创建项目。同时可以在Visual Studio中运行Flex程序,支持调试断点和堆栈信息显示,并且将会在Visual Studio的错误信息列表中显示错误信息。Tofino将会通过开源的Flex框架的形式向用户发放。

“我们知道许多的 Visual Studio开发者希望使用Flex去开发同类最佳的程序,同时也希望使用他们已经非常熟悉的工具”,Adobe 的产品经理Greg DeMichillie说,”我们非常荣幸地介绍Ensemble  Tofino,因为它使得Visual Studio开发者充分地使用Flex去开发企业级应用程序”.

目前Ensemble Tofino还是第一个Beta版本,同时后续的开发版本将持续得提高开发体验。更多的信息和下载在:http://www.ensemble.com

关于Ensemble

Ensemble (www.ensemble.com) 是一个Adobe的开发伙伴,专注于需求分析,体系结构,执行,部署所有的Adobe技术。特别是在Adobe(R) LiveCycle(R), Adobe(R) Flex(R), Adobe(R) AIR(TM)和Adobe(R) Acrobat(R)的集成方案.在政府,金融服务,媒体,公共出版和制造业部署Adobe技术拥有非常丰富的经验。基于 Vancouver,British Columbia,我们满足世界上任何地方人们的需要。

Tofino(82.59MB)下载:

http://www.ensemble.com/downloadables/products/Tofino/EnsembleTofino.msi

Attention:只支持Visual Studio 2008 /Windows Vista/XP