2011年11月25日

Pro WPF in C# - think aloud

刚开始看,正看到第三章Layout,真搞不懂WPF一直强调的设计UI的那些改变真的是好事吗?

首先,有多少开发人员真的关心一个窗口改变大小带来的影响呢?开发人员完全可以将主窗体设为默认最大化,所有功能都用不能调整大小的弹出窗口,或者Dock在一个固定大小的Panel里面。用户调整主窗口大小?吃饱了撑的啊,能干事就行,没事调整大小干嘛。 自己要调整大小,就别怪大小改变导致界面变形不规则什么的(何况Winform调整窗口大小只要不过分也不会有多难看的效果)。Winform做出来的应用程序又不是让用户当游戏一样玩的。

再说不同显示器和分辨率的影响。.NET出现的时候,大概流行的显示器还是15寸的。后来显示器一直在发展,Winform应用程序也在不断地被部署到客户那里。有多少客户会抱怨说,因为显示器换了更好的,导致以前你们给开发的应用程序不好用了,需要更新程序?我孤陋寡闻,没听过。

这是没事找事做的无用功,再说说做的反向功。

我刚开始看WPF,也许这个问题不像我想象的那么麻烦。但就我目前的理解,控件的位置和大小都用固定像素和固定坐标来表示是很直观,很简单,很好理解和维护的方式,为什么要求用户都接受流方式的布局呢?我个人喜好CS多于BS就是因为不想花太多心思在网页的布局上面。div+css真的不是我喜欢干的事情,不知道什么时候需要clear both,不知道什么时候用float,什么时候用relative position,一想起这些我就头大。相反,Winform的布局真的是所见即所得。我可以非常轻松地做到把四个TextBox放在一个GroupBox里面,让它们在一条横线上,中间等距离宽,看起来不说很漂亮,至少简洁大方,用户也能够轻松地理解。WPF?好嘛,先得想想是用StackPanel,WrapPanel,还是Grid什么的。拜托,我又没有做表格,非让我在Grid里面设计干嘛,即使它是不可见的。想想用Winform要显示一个俱乐部客户的详细信息,拖一把基础控件显示基本信息,加上几个标签,每个标签显示某个特定方面的数据,就完了。用WPF?先得想想一共需要多少个Container,哪些控件放在哪个Container里面,哪些Container又放在一个更大的Container里面。如果需要对齐,还得想想用Margin,Alignment分别设置成什么。累啊,也许熟练了之后不觉得累,但是一个是学习需要时间,更重要的是我为什么要去花时间学习,理解,练习,然后掌握一个跟我核心工作不相关的事情呢?

开发人员的重点应该放在系统架构上,想想怎么设计项目结构,有几个Project,每个是干什么用的。类的层次是怎么样的,哪些类用抽象的,哪些要定义接口,等等。 

有多少CS的项目是客户强烈要求界面漂亮到觉得自己干不了,只能去买商业第三方的漂亮控件呢?第三方的控件是漂亮了,我就遇到过由于使用Infragistics的控件导致内存泄漏的问题。类似这种问题在控件开发公司的论坛上一直就没断过。为了追求一点点的漂亮,给程序开发和维护造成很大的影响,真的值得吗?

目前来说,我继续学习WPF的目的可能是为了能在Winform中加入部分WPF控件来局部美化界面吧。 

posted @ 2011-11-25 09:43 buaaytt 阅读(32) 评论(0) 编辑

2005年11月23日

VS2005的一个bug

今天在调试代码的时候发现了一个bug,本来打算feedback给MS,不过一搜索,才发现已经有人报告过这个bug了,而微软说在现在这个版本不会解决。Anyway,this is just FYI: http://lab.msdn.microsoft.com/productfeedback/viewfeedback.aspx?feedbackid=d36ae5cf-07f6-4c10-b40f-97a7b6a7ce0c

posted @ 2005-11-23 16:42 buaaytt 阅读(371) 评论(0) 编辑

2005年11月6日

翻译MSDN文章 —— 泛型FAQ:最佳实践

原文:http://msdn.microsoft.com/netframework/default.aspx?pull=/library/en-us/dndotnet/html/BestPractices.asp 

作者:Juval Lowy(后面介绍中说微软将此人视为Software Legend as one of the world's top .NET experts and industry leaders,名头很响亮啊。无奈本人孤陋寡闻,没听过)

注:括号里的英文是我认为不好翻译的原文,如果您有更好的译法,请告诉我,谢谢。同时,对包含generic的组合词语(如generic type parametergeneric method)参照MSDN中文网站C# 泛型简介一文统一译作“一般”,虽然我认为并不好听。
 

摘要

回顾关于使用泛型最佳实践的FAQ

内容

什么时候我不应该使用泛型?
对泛型我应该使用什么命名规范?
我应该在泛型接口上面添加约束吗?
如何处置(Dispose)泛型接口?
可以对一般类型参数进行类型转换吗?
对泛型类如何同步多线程访问?
如何序列化泛型类?

什么时候我不应该使用泛型?

        不使用泛型的主要原因就是跨目标(cross-targeting)——如果你要在.NET 1.1和.NET 2.0下编译相同的代码,那么由于只有.NET 2.0支持泛型,你就不能够使用泛型。

对泛型我应该使用什么命名规范?

       
我建议使用一个单独的大写字母来表示一般类型参数。如果你对类型参数没有其他的跟上下文有关的信息(additional contextual information),你应该使用字母T

[C#]

public class MyClass<T>
{...}

[Visual Basic]

Public Class SomeClass(Of T)
   ...
End Class

[C++]

generic <typename T>
public ref class MyClass
{...};

        在所有其他场合下,微软正式的对泛型的的命名规范指导是:

  • 一般类型参数要有描述性的名字,除非一个单独的字母已经表示得很清楚,再增加描述性的名字也没有多大用处。

[C#]

public interface ISessionChannel<TSession> 
{...}
public delegate TOutput Converter<TInput,TOutput>(TInput from);

[Visual Basic]

Public Interface ISessionChannel(Of TSession)
   ... 
End Interface

Public Delegate Function Converter(Of TInput, TOutput)(ByVal input As TInput) As Toutput

[C++]

generic <typename TSession>
public interface class ISessionChannel 
{...};
generic <typename TInput, typename TOutput>
public delegate TOutput Converter(TInput from);
  • 可以考虑在一般类型参数的名字中表示出添加给该一般类型参数的约束。例如,一个被约束到ISession接口的参数可以起名为TSession

我应该在泛型接口上面添加约束吗?

        接口可以为其使用的范型类型添加约束,例如: 

[C#]

public interface ILinkedList<T> where T : IComparable<T>
{...}

[Visual Basic]

Public Interface ILinkedList(Of T As IComparable(Of T))
   ...
End Interface

[C++]

generic <typename T> where T : IComparable<T>
public interface class ILinkedList
{...};

        但是,你应该小心,在接口层面上定义约束还隐含有另外一层意思。为了强调接口与实现分离的思想,接口不应该包括任何一点实现的细节。虽然有很多方法可以用来实现范型接口,但是使用特定的类型参数毕竟是一种实现的细节。约束通常情况下会更加耦合(couple)接口和特定的实现。

        更好的方法是,为实现范型接口的类添加约束,保持接口本身没有约束:

[C#]

public class LinkedList<T> : ILinkedList<T> where T : IComparable<T>
{
   //Rest of the implementation  
}

[Visual Basic]

Public Class LinkedList(Of T As IComparable(Of T))
       Implements ILinkedList(Of T)
' Rest of the implementation  
End Class

[C++]

generic <typename T> where T : IComparable<T> 
public ref class LinkedList : ILinkedList<T>
{
   //Rest of the implementation  
};

如何处置(Dispose)泛型接口?

        在C#Visual Basic中,如果你把一个一般类型参数的对象放在using语句中,编译器无法知道客户端(client)指定的实际类型是否支持IDisposable接口。因此编译器不允许在using语句中使用一般类型参数的实例。

[C#]

public class MyClass<T> 
{
   public void SomeMethod(T t)
   {
      using(t)//Does not compile 
      {...}
   }
}

[Visual Basic]

Public Class SomeClass(Of T)
   Public Sub SomeMethod(ByVal value As T)
      Using value ' Does not compile
      End Using
   End Sub
End Class

        当然,你可以强制约束类型参数支持IDisposable接口:

[C#]

public class MyClass<T> where T : IDisposable 
{
   public void SomeMethod(T t)
   {
      using(t)
      {...}
   }
}

[Visual Basic]

Public Class SomeClass(Of T As IDisposable)
   Public Sub SomeMethod(ByVal value As T)
      Using value
      End Using
   End Sub
End Class

        但是你不应该这么做。这样做的问题在于你不能使用接口作为类型参数了,即使这个接口的基础类型(underlying type)支持IDisposable也不行:

[C#]

public interface IMyInterface
{}
public class MyOtherClass : IMyInterface,IDisposable 
{...}
public class MyClass<T> where T : IDisposable 
{
   public void SomeMethod(T t)
   {
      using(t)
      {...}
   }
}
MyOtherClass myOtherClass = new MyOtherClass();
MyClass<IMyInterface> obj = new MyClass<IMyInterface>();//Does not compile
obj.SomeMethod(myOtherClass); 

[Visual Basic]

Public Interface IMyInterface
End Interface

Public Class MyOtherClass
      Implements IMyInterface, IDisposable
   ...
End Class

Public Class SomeClass(Of T As IDisposable)
   Public Sub SomeMethod(ByVal value As T)
      Using value
      End Using
   End Sub
End Class

Dim myOtherClass As New MyOtherClass
Dim obj As New SomeClass(Of IMyInterface) ' Does not compile
obj.SomeMethod(myOtherClass)

        作为替代,我建议你在using语句里对一般类型参数使用C#中的as操作符或者Visual Basic中的TryCast操作符来允许接口作为一般类型参数使用:

[C#]

public class MyClass<T> 
{
   public void SomeMethod(T t)
   {
      using(t as IDisposable)
      {...}
   }
}

[Visual Basic]

Public Class SomeClass(Of T)
   Public Sub SomeMethod(ByVal value As T)
      Using TryCast(value, IDisposable)
      End Using
   End Sub
End Class

可以对一般类型参数进行类型转换吗?

        对于隐式转换,编译器只允许将一般类型参数转换为object类型,或者其约束里指定的那个类型:

[C#]

interface ISomeInterface
{...}
class BaseClass
{...}
class MyClass<T> where T : BaseClass,ISomeInterface
{
   void SomeMethod(T t)
   {
      ISomeInterface obj1 = t;
      BaseClass      obj2 = t;
      object         obj3 = t;
   }
}

[Visual Basic]

Interface ISomeInterface
   ...
End Interface

Class BaseClass
   ...
End Class

Class SomeClass(Of T As{BaseClass,ISomeInterface})

   Private Sub SomeMethod(ByVal value As T)
      Dim obj1 As ISomeInterface = value
      Dim obj2 As BaseClass = value
      Dim obj3 As Object = value
   End Sub
End Class

[C++]

interface class ISomeInterface
{...};
ref class BaseClass
{...};
generic <typename T> where T : BaseClass,ISomeInterface
ref class MyClass
{
   void SomeMethod(T t)
   {
      ISomeInterface ^obj1 = t;
      BaseClass      ^obj2 = t;
      Object         ^obj3 = t;
   }
};

        这种隐式转换当然是类型安全的,因为无效的转换在编译时就会被发现。

        对于显示转换,编译器允许将一般类型参数转换到任何接口,但是不能转换为类:

[C#]

interface ISomeInterface
{...}
class SomeClass
{...}
class MyClass<T> 
{
   void SomeMethod(T t)
   {
      ISomeInterface obj1 = (ISomeInterface)t;//Compiles
      SomeClass      obj2 = (SomeClass)t;     //Does not compile
   }
}

[Visual Basic]

Interface ISomeInterface
   ...
End Interface

Class BaseClass
   ...
End Class

Class SomeClass(Of T)
   Private Sub SomeMethod(ByVal value As T)
      Dim obj1 As ISomeInterface = CType(value,ISomeInterface)' Compiles
      Dim obj2 As BaseClass = CType(value,BaseClass)' Does not compile
   End Sub
End Class

[C++]

interface class ISomeInterface
{...};
ref class SomeClass
{...};
generic <typename T>
ref class MyClass 
{
   void SomeMethod(T t)
   {
      ISomeInterface ^obj1 = (ISomeInterface ^)t;//Compiles
      SomeClass      ^obj2 = (SomeClass ^)t;     //Does not compile
   }
};

        但是,你可以通过使用一个临时的object类型变量来强制将一般类型参数转到到任何其他类型:

[C#]

class MyOtherClass
{...}

class MyClass<T> 
{
  
   void SomeMethod(T t)
   
   {
      object temp = t;
      MyOtherClass obj = (MyOtherClass)temp;
   
   }
}

[Visual Basic]

Class MyOtherClass
   ...
End Class

Class SomeClass(Of T)
   Sub SomeMethod(ByVal value As T)
      Dim temp As Object = value
      Dim obj As MyOtherClass = CType(temp, MyOtherClass)
   End Sub
End Class

[C++]

ref class SomeClass
{...};

generic <typename T>
ref class MyClass 
{
  
   void SomeMethod(T t)
   
   {
      Object ^temp = t;
      SomeClass ^obj = (SomeClass ^)temp;
   
   }
};

        毫无疑问,这样的显示转换是很危险的,因为如果实际使用的替代一般类型参数的类型不是从你要转换到的类型那里继承的话,就可能在运行时抛出异常。

[C#]

        为了避免这种转换时有异常的风险,一个更好的办法是使用is或者as操作符。如果一般类型参数是(is)要查询的类型,is 操作符会返回true,而as操作符会在两个类型兼容的时候执行转换,否则将返回null

public class MyClass<T>
{
   public void SomeMethod(T t)
   {
      if(t is int)
      {...}

      if(t is LinkedList<int,string>)
      {...}

      string str = t as string;
      if(str != null)
      {...}

      LinkedList<int,string> list = t as LinkedList<int,string>;
      if(list != null)
      {...}
   }
}

[Visual Basic]

        为了避免这种转换时有异常的风险,一个更好的办法是使用TypeOfTryCast操作符。如果一般类型参数是(is)要查询的类型,is 操作符会返回true。你也可以使用TryCast操作符在两个类型兼容的时候执行转换,否则就返回Nothing

Class SomeClass(Of T)

   Public Sub SomeMethod(ByVal value As T)
      If TypeOf value Is Integer Then
         ...
      End If

      If TypeOf value Is LinkedList(Of Integer, String) Then
         ...
      End If

      Dim str As String = TryCast(value,String)
      If (Not str Is Nothing) Then
         ...
      End If

      Dim list As LinkedList(Of Integer, String) = _
         TryCast(value,LinkedList(Of
         Integer, String))
      If (Not list Is Nothing) Then
         ...
      End If
   End Sub
End Class

对泛型类如何同步多线程访问?

        通常来说,你不应该在一般类型参数上应用Monitor。这是因为Monitor只能用于引用类型。当你使用范型的时候,编译器不能预先判断你将会提供一个引用类型还是值类型的类型参数。在C#中,编译器会允许你使用lock语句,但是如果你提供了一个值类型作为类型参数,lock语句在运行时将不起作用。在Visual Basic中,编译器如果不能确定一般类型参数是一个引用类型,它将不允许在一般类型参数上面使用SyncLock

        在C#Visual Basic中,唯一你可以安全地将一般类型参数锁住的时候,是你将一般类型参数限制为引用类型,要么添加约束使其为引用类型,要么从一个基类中继承:

[C#]

public class MyClass<T> where T : class
{..}

[Visual Basic]

Public Class SomeClass(Of T As Class)
   ...
End Class

或者:

[C#]

public class SomeClass
{...}
public class MyClass<T> where T : SomeClass
{...}

[Visual Basic]

Public Class SomeClass
   ...
End Class
Public Class SomeClass(Of T As SomeClass)
   ...
End Class

        然而,通常对于同步来说,最好避免部分地锁住单独的成员变量,因为这会增加死锁的可能性。

如何序列化泛型类?

        包括了一般类型参数作为成员的范型类是可以被标记为序列化的:

[C#]

[Serializable]
public class MySerializableClass<T>
{
   T m_T;
}

[Visual Basic]

<Serializable()> _
Public Class MySerializableClass(Of T)

   Dim m_T As T
End Class

[C++]

generic <typename T>
[Serializable]
public ref class MyClass
{
   T m_T;
};

        但是,在这种情况下,只有指定的类型参数可以被序列化时,范型类才可以被序列化。看下面的代码:

[C#]

public class SomeClass
{}
MySerializableClass<SomeClass> obj;

[Visual Basic]

Public Class SomeClass
End Class

Dim obj as MySerializableClass(Of SomeClass)

[C++]

public ref class SomeClass
{};
MyClass<SomeClass ^> ^obj;

        obj不能被序列化,因为类型参数SomeClass不可以被序列化。因此,MySerializableClass<T>可能可以,也可能不可以被序列化,取决于使用的一般类型参数。这样可能导致在运行时丢失数据或者系统崩溃,因为客户应用程序可能不能够保持对象的状态。

        目前,.NET没有提供将一般类型参数约束为可序列化的机制。解决办法是在运行时在使用这个类型之前单独进行检查,并且在任何损害发生之前马上中止使用。你可以把这个运行时的验证放在静态构造器里面:

[C#]

[Serializable]
class MySerializableClass<T>
{
   T m_T;

   static MySerializableClass()   
   {
      ConstrainType(typeof(T));
   }
   static void ConstrainType(Type type)
   {
      bool serializable = type.IsSerializable;
      if(serializable == false)
      {
         string message = "The type " + type + " is not serializable";
         throw new InvalidOperationException(message);
      }
   }
}

[Visual Basic]

<Serializable()> _
Class SomeClass(Of T)
   Private m_T As T   

   Shared Sub New()
      ConstrainType(GetType(T))
   End Sub
   Private Shared Sub ConstrainType(ByVal t As Type)
      If Not t.IsSerializable Then
         Dim message As String = "The type " + t.ToString() + " is not 
                                                         serializable"
         Throw New InvalidOperationException(message)
      End If
   End Sub
End Class

[C++]

generic <typename T>
[Serializable]
ref class MyClass
{
   T m_T;
public:   
   static MyClass()   
   {
      ConstrainType(typeid<T>);
   }
private:
   static void ConstrainType(Type type)
   {
      bool serializable = type->IsSerializable;
      if(serializable == false)
      {
         String ^message = String::Concat("The type ", type->Name, 
            " is not serializable");
         throw gcnew SerializationException(message);
      }
   }
};

        静态构造器对每一个应用程序域的每一个类型只执行一次,而且是在类型第一次被请求实例化之前。尽管你有一些通过编程的方式来在运行时进行判断和执行检查,但是这种在静态构造器里面执行约束验证的技术,对任何无法在编译时进行检查的约束都适用。

posted @ 2005-11-06 16:20 buaaytt 阅读(2075) 评论(0) 编辑

2005年10月28日

.NET Framework 2.0 以及SDK可以下载了

今天收到的邮件,.NET Framework 2.0和SDK的正式版提供下载了。下载地址在这里:
http://www.microsoft.com/downloads/results.aspx?productId=C9C8FCFB-BFF3-40CA-B59D-216F6850000A&CategoryID=&freetext=framework&DisplayLang=en&DisplayEnglishAlso=&sortCriteria=date&startDate=&period=0&type=&nr=20
有三个版本,x86,x64和IA64。Team Foundation Server的正式版还得等到明年初。

posted @ 2005-10-28 09:58 buaaytt 阅读(1261) 评论(0) 编辑

2005年7月28日

让窗体从初始时就保持最大化,并且不能改变窗体大小的完美解决方案

摘要: 我的要求就是让窗体从初始化的时候就最大化,并且用户不能改变窗体大小。最开始的想法是捕获窗体大小改变,即ReSize事件,然后在事件里面把窗体大小设置成初始时的大小。完整的Form.cs代码如下: usingSystem; usingSystem.Drawing; usingSystem.Collections; usingSystem.ComponentModel; usingSystem.Win...阅读全文

posted @ 2005-07-28 14:24 buaaytt 阅读(1912) 评论(1) 编辑

2005年7月12日

.NET执行==运算符时的转换处理

摘要: 这个问题我最早发在CSDN论坛上,经过很多网友的热心帮助,最后我得出了一个结论。对不对我不知道,但是我有相当的自信是这样的,希望有人能告诉我吧。 例子代码如下: usingSystem; usingSystem.Collections; structDigit { bytevalue; publicDigit(bytevalue) { if(value>9)thrownewArgumentE...阅读全文

posted @ 2005-07-12 16:24 buaaytt 阅读(1561) 评论(8) 编辑

2005年6月24日

我觉得VS2005的Refactor应该提供泛型到非泛型之间的转换

摘要: 比如当前有一个类是泛型,现在我不想要泛型了,而且改变之后不会对其他代码产生很大的影响,这时应该可以通过Refactor来帮助把代码中的<>以及相关部分去掉,当然之前会要求你输入一个特定类型来取代泛型中的一般类型。同样,如果想把当前某个类型改成泛型,Refactor也应该可以做到,只需要用一般类型替换掉当前的特定类型就可以了。阅读全文

posted @ 2005-06-24 12:25 buaaytt 阅读(359) 评论(0) 编辑

2005年6月21日

MSDN中文网站关于翻译有很严重的问题

摘要: 就是内容翻译得尚可,但是代码简直是没法看。我每次看中文翻译的文章的时候遇到代码,都要转到相应英文页面去看代码。不知道为什么中文翻译里的代码和原文里的代码相差那么大,很多时候都会严重影响阅读。 看看这篇MSDN翻译里面的代码块就能理解了:http://www.microsoft.com/china/msdn/library/langtool/vcsharp/csharpgenerics.mspx阅读全文

posted @ 2005-06-21 15:09 buaaytt 阅读(423) 评论(0) 编辑

2005年6月17日

使用System.StringSplitOptions去除待拆分字符串中的空项

摘要: 很简单的东西,今天学会了,就记下来了。当我们使用某个字符来将一个字符串拆分到字符串数组中的时候,如果待拆分的字符串中有空项,也就是 "" 的时候,在结果数组中也会出现。如果不想将空项包含在结果数组中,就可以使用.NET 2.0的一个新的枚举StringSplitOptions来设置。具体代码如下:strings="0,1,2,";string[]res=s.Split(newchar[]{','}...阅读全文

posted @ 2005-06-17 12:55 buaaytt 阅读(1145) 评论(1) 编辑

2005年2月23日

Remoting中在客户端使用自定义的Sponsor

摘要: 很是郁闷,本来写好了,修改提交了几次又没了一大半,只好简单地重新写一点了。 我说话比较喜欢简洁,直接看代码吧(例子根据《Advanced .NET Remoting》一书而来): Server端: Using directives#region Using directives using System; using System.Collect...阅读全文

posted @ 2005-02-23 12:08 buaaytt 阅读(1567) 评论(0) 编辑