2016年3月

小知识点集锦

一、 浮点数和0的比较
今天在做五参数几个数值处理的时候,因为为0的值要舍去,刚开始写了一行代码


if(x != 0){
//xxx
}

后来一想,不大对啊,感觉不对。上网一搜,上面的是大忌
正确的做法

if(x <= 0.000001 && x >= -0.000001)

原因:

float存在精度问题。整数部分越大,小数部分的精度就越低。0.100.1是两个不同的数。

二、 ListBox绑定到List集合或ObservableCollection,(问题仍然没有解决)
仅仅是数据绑定List上的话非常简单

public List<int> PhList = new List<int>(){1,2,3,4,5,6};
    public ObservableCollection<int> PhCollection = new ObservableCollection<int>(){1,2,3,4,5,6};

    public int Flag = 0;

    public MainWindow()
    {
        InitializeComponent();
        LbPhList.ItemsSource = PhList;
//      LbPhList.ItemsSource = PhCollection;
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        Flag ++;
        PhList.Add(Flag);
//      PhCollection.Add(Flag);
    }

    private void Button_Click_1(object sender, RoutedEventArgs e)
    {
        MessageBox.Show(Flag.ToString());
    }

现在的问题是:当我往集合中添加数据的话,ListBox并不会自动更新,这里就引出来了ObserableCollection,取消上上述代码的注释,可以看到ListBox可以更新数据了。
为什么:

这就是为什么ObservableCollection<T>会存在。ObservableCollection<T>实现了INotifyCollectionChanged,允许去通知UI集合中的数据被添加、删除或者替换了。List<T>没有触发任何通知,所以UI不能检测里面的内容发生了变化。来自stackoverflow

问题又来了:在Code Behind文件下没有任何问题,可以用上面代码的方式通知UI进行更新,在MVVM模式下,不起作用了。使用ObservableCollection也不行。(因为这个东西已经快疯掉了)
出现异常成为我的最爱:

对此问题无效现在我想把我的操作系统换成英文的了

三、 函数调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配。

改 [DllImport("xxxx.dll", EntryPoint = "xx",)]
为[DllImport("xxxx.dll", EntryPoint = "xx", CallingConvention = CallingConvention.Cdecl)]
四、WPF中双击事件
MouseDown的事件里直接判断

    if(e.ClickCount ==2){
        //双击事件
    }

五、C# Float和double区别
float关键字表示存储32位浮点值得简单类型。默认情况下,赋值运算符右侧的实数被视为double。因此,应该使用fF出事啊花浮点型变量

float x = 3.5f;

如果不使用后缀,则会因为尝试将一个double值存储在float变量中而发生编译错误。
所有的浮点型变量都存在精度损失的问题。double是双精度,double保存32位,float保存16位。
六、MVVMLight 传递参数
How to uas RelayCommand with the MVVM Light framework,最重要的就是PassEnevtArgsToCommand = "True"
七、键盘控制


未完待续

记录一下Mvvm light toolkit 的使用

学习了两个周的MVVM Light Toolkit,来稍微回顾一下吧。

首先是相关的网站

MVVM Light Toolkit 官方网站
MVVM Light Toolkit 作者的个人博客
MVVM Light Toolkit 作者在MSDN的文章,这才是精髓
Prism 框架,里面有部分介绍到了MVVM
博客园链接,里面自己也可以搜到几篇关于MVVM 的文章
CodeProject上的文章,太清楚了,推荐

MVVM设计模式

上一年看了一年的MVVM,也最后没搞明白,开学以来学习了两个周的MVVM Light Toolkit就体会到了MVVM的方便,废话不多少,正式开始(以下全部为个人理解)。
MVVM是从MVC发展而来(~当前听到的第一个框架就是MVC,还啥也不懂,就是到现在也没用过两次,手动尴尬),是Model View ViewModel的简称。看下面两张图,我觉得这是最能展现MVVM设计模式精髓的图
来自prism msdn

来自 Laurent Bugnion 在MSDN的文章

可以看到在ViewModel之间多了一个ViewModel,以往做WPF程序设计时,所有的程序逻辑代码全部在CodeBehind文件中,现在挪到了ViewModel中,ViewModel来实现ViewModel的通信。ViewViewModel中间就是通过WPF程序设计的精髓 “数据绑定” 机制 和 “命令绑定” 机制,即Binding和Command

贴一段来自Prism的关于MVVM的介绍

The Model-View-ViewModel (MVVM) pattern helps you to cleanly separate the business and presentation logic of your application from its user interface (UI). Maintaining a clean separation between application logic and UI helps to address numerous development and design issues and can make your application much easier to test, maintain, and evolve. It can also greatly improve code re-use opportunities and allows developers and UI designers to more easily collaborate when developing their respective parts of the application.
Using the MVVM pattern, the UI of the application and the underlying presentation and business logic is separated into three separate classes: the view, which encapsulates the UI and UI logic; the view model, which encapsulates presentation logic and state; and the model, which encapsulates the application's business logic and data

这样做的好处有什么呢?
上面也提到了就是使你的业务逻辑和展示逻辑分开,美工和程序员各司其职(对大部分WPF开发者来说没有美工吧),使得程序更容易测试、维护。使得代码的可重用性增强。反正即使好处多多。

我们以前怎么使用数据绑定呢?

不使用MVVM Light Toolkit以前的时候使用数据绑定你需要这么做。

//首先实现INotifyPropertyChanged接口,如果使用了Resharper工具,那么实现其中的方法会帮你自动生成代码
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
  {
      PropertyChangedEventHandler handler = PropertyChanged;
      if (handler != null)
      {
          handler(this, new PropertyChangedEventArgs(name));
      }
  }

private int someMember;

public int SomeMember
  {
  get
  {
      return this.someMember;
  }
  set
  {
      if (this.someMember != value)
      {
          someMember = value;
          OnPropertyChanged("Some Member");
      }
  }
  }

MVVM Light Toolkit工具不过是帮我们把这些代码写了,我们只需要实现ViewModelBase接口就可以,使用RaiseProperty("属性名")来使得属性具有通知属性。
还有就是命令Command如何来绑定呢?这里推荐使用Blend,这个工具真的是太好用了。有了它我再也不去写那么一坨我记不住的绑定

Command = {Binding myCommand};
或者
使用Interaction(这里记不住,我都直接用Blend拖拖生成)

View和View之间如何交互

如果直接在ViewModel中去New一个窗口弹出,这样肯定是不符合MVVM设计模式
MVVM Light Toolkit提供了很好的解决方案,就是MessageService

//Message有注册和发送功能
//注册一个消息
Messenger.Default.Register<StatusMessage代表传入的消息类型>(this,"key", (m)={
    //处理事件
});

//在其他地方就可以发送消息
Messenger.Default.Send(new StatusMessage("Getting articles", "key"));//上面的地方就收到了这条消息,可以对其进行处理

//当然也可以取消注册
Messenger.Default.Unregister<StatusMessage>(this,()=>{
    //处理事件
});

平时我都是给消息加一个唯一标识,就上面的key,这样发送和接受我都知道谁会收到。
另外一种实现就是Service了,这种实现也很优雅

//具体来说就是有那么一种工具类,专门来做某件事,比如弹出窗口
public interface IWindowService{
    public void ShowStatus(Window window); 
}

public class WindowService:IWindowService{
    public void ShowStatus(Windows window){
        windows.Show();
    } 

//在其他地方
public Class xxViewModel{
    public IWindowService _windowsService;
    public xxViewModel(IWindowService windowService)
    {
        _windowService = windowService;
    }

    public xxMethod(){
        ...
        _windowService.Show(New xxWindow());
    }
}

使用Service的好处是比Messenger好调试Messenger发送出去了,如果其他人来修改代码的话常常不知道谁接收到了这个消息,对这个消息做了处理。

IOC容器

这里面提到了一个概念是IOCInversion of Control简称,又是一个高大上的名词,我不懂啥意思。好像比较著名的有微软的Unity框架就是干这个的(不懂),但我简单的会使用SimpleIoc(这是MVVM LIght Toolkit提供的IOC框架)

//有个文件是ViewModelLocator.cs
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
if (ViewModelBase.IsInDesignModeStatic)    //判断是在设计模式还是在运行模式
{
  SimpleIoc.Default.Register<IRssService, Design.DesignRssService>();
}
else
{
  SimpleIoc.Default.Register<IRssService, RssService>();
}
SimpleIoc.Default.Register<INavigationService, NavigationService>(); //注册服务
SimpleIoc.Default.Register<MainViewModel>();
 
public MainViewModel Main
{
    get
    {
      return ServiceLocator.Current.GetInstance<MainViewModel>();
    }
}   
在APP.XAML中有这么一行代码
<vm:ViewModelLocator x:Key="Locator"
                         d:IsDataSource="True" />
//然后你就可以在你的View中绑定ViewModel了
DataContext = "{Main, Source = {Binding Locator}}"    //大体应该是这样。

设计模式--创建者模式

创建者模式概述

创建者模式的精髓部分死实现对复杂对象反复构造的过程。适用的场景是:产品局部加工过程很大,但组装过程相对固定。
其中经典模式主要包括三个部分:

  1. Builder

描述创建一个产品各个组成的抽象接口。

  1. Concrete Builder

实现Builder要求的内容,提供获得一个获得最终产品的方法。

  1. Director

指导产品加工的步骤,但指导完全基于Builder的抽象方法。

我们先来看一个经典的案例,需求如下:

  • 制造一些交通哦刚那句,这些工具有轮子也有车灯

  • 现有加工要求中,汽车需要四个轮子(前后各两个)、四盏车灯(前后各两个),自行车两个轮子(前后各一个),没有车灯。

//C# 定义目标产品和抽象的创建者类型
public class Vehicle{
    public IEnumerable<string> Wheels{get;set;}
    public IEnumerable<string> Lights{get;set;}
}

public abstract class VehicleBuiderBase{
    public Vehicle Vehicle{get; private set;}
    public Virtual void Create(){Vehicle = new Vehicle(); }
    public abstract void AddWheeles();
    public abstract void AddLights();
}

//C# 定义的具体的创建者类型
public class CarBuilder:VehicleBuilderBase{
    #region VehicleBuilderBase Members

    public override void AddWheels(){
        Vehicle.Wheels = new String[]{"front", "front" ,"back", "back"}
    }

    public override void AddLights(){
        Vehicle.Lights = new string[]{"front", "front", "back", "back"}
    }

    #endregion
}

public class BicycleBuilder:VehicleBuilderBase{
    #region VehicleBulderBase Members

    public override void AddWheels(){
        Vehicle.Wheels = new String[] {"front","back"}
    }

    public override void AddLights(){
        Vehicle.Lights = null;
    }

    #endregion
}

接下来是定义实际指导创建者进行生产的Director

public class VehicleMaker{
    public VehicleBuilderBase Builder{get;set;}

    public Vehicle Vehicle{ get{ return Builder.Vehicle; } }

    public void Construct(){
        Builder.Create();
        Builder.AddWheels();
        Builder.AddLights();
    }
}

客户端如何使用呢:

public void BuildUp(){
    var marker = new VehicleMaker();
    marker.Builder = new CarBuilder();
    marker.Construct();
    Vehicle vehicle = marker.Vehicle();    //这样就得到了构建的汽车
}

可以看到使用创建者模式向客户程序屏蔽了对象创建过程的多边形,同样是经过一系列创建过程,创建汽车的具体过程客户端不用知道,而放在了CarBuilder中。

总结

以上就是经典的创建者模式,在书上还讲了使用一个工具类为Builder打上标签使Builder具有装配、卸载能力,还暂时理解不了,就不写了。
主要优点:

  1. 建造者模式中,客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程都可以创建不同的产品对象。

  2. 每一个具体建造者都相对独立,而与其他具体建造者无关,因此可以很方便的替换具体创建者或者添加新的具体建造者。

  3. 可以更加精细的控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更加方便使用程序来控制创建过程。

主要缺点:

  1. 建造者所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,就不适合使用创建者模式,因此其使用范围收到一点点限制。

  2. 如果产品的内部结构复杂而多变,可能会需要定义许多具体构建着来实现这种变化,这会导致系统变得很庞大,增加理解难度和运行成本。


设计模式--原型模式

原型模式概述

如何在一个面向对象的系统中实现对象的复制和粘贴呢?本文将的原型模式正式解决这个问题。
原型模式

使用原型实例指定创建对象的种类,并且通过克隆这些原型创建新的对象。原型模式是一种对象创建型模式
原理就是讲一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象克隆自己来实现创建过程。

在原型模式中包含以下三种角色:

  1. (Prototype)抽象原型类:它是生命克隆方法的接口,是所有具体圆形累的公共父类。

  2. (ConcretePrototype)具体原型类:它实现抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个克隆对象。

  3. (Client)客户类:让一个原型对象克隆自身从而创建一个新的对象,在客户类中只需要实例化或通过工厂方法等方式创建一个原型对象,调用克隆方法就可以得到多个相同的对象。

原型模式.NET Framework内置的一个模式,下面为实现的一个经典范例:

public interface IPrototype{
    IPrototpe Clone();
    string Name{get;set;}
}

public class ConcretePrototype:Iprototype{
    public IPrototype Clone(){
        return (IPrototype)this.MemberwiseClone();
    }
    public string Name{get;set;}
}

//客户端如何使用
public void Initialize(){
    sample = new ConcretePrototype();
}

public void Test(){
    var image = sample.Clone();
}

分析:

  • 克隆的副本具有和样本一样的类型,而且状态也一致

  • 样本于副本是两个独立的实例,在克隆结束后,它们之间可以互相独立变化

上面仅仅通过Object.MemberwiseClone完成了克隆,但这仅仅对部分成员类型有效,我们看下修改后的情况。

public class Indicator{}

public interface IPrototype{
    IPrototype Clone();
    Indicator Signal{get;}
}

public class ConcretePrototype:IPrototype{
    public IPrototype Clone(){
        return (IPrototype)this.MemberwiseClone();
    }

Indicator signal = new Indicator();
public Indicator Signal {
    gtet{
            return this.signal;
        }
    }
}

如果还是调用Clone方法,那么样本和副本的Indicator是同一个使用,这就是浅复制浅复制对引用对象只复制引用,而不复制引用的对象(也就是仅仅保存一个指针)。因此样本和副本中引用类型成员指向的是同一个对象。


深度复制现在先不讨论了。

总结

原型模式作为一种快速创建大量相同或相似对象的方式,在软件开发中应用较为广发。
主要优点:

  1. 当创建的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过复制一个已有实例可以提高新实例的创建效率

  2. 扩展性较好。由于在原型模式中提供了抽象原型类,在客户端可以针对抽象原型类进行编程,而将具体原型类写在配置文件中,增加或减少具体原型类都对原有系统没有影响。

  3. 可以使用深克隆的方式保存对象的状态,以便在需要的时候进行恢复。

缺点:

  1. 需要为每个类配备一个克隆方法,而且克隆方法位于一个类的内部,当对已有的类进行改造时,需要修改源代码,违背了开闭原则。

  2. 在实现深克隆时需要编写较为复杂的代码