存档

作者存档

C#基础:Lambda表达式

2011年8月28日 没有评论

从委托的角度来看,Lambda表达式与匿名方法没有区别。从C# 3.0开始,在使用匿名方法的地方,完全可以用Lambda表达式来代替。Lambda表达式的定义方式为:“([参数列表]) => 表达式”。运算符“=>”是一种与赋值运算“=”具有相同优先级的右结合运算符,在英语里读作:“goes to”。

view plaincopy to clipboardprint?

    class Program
    {
        static void Main(string[] args)
        {
            List<string> names = new List<string>();
            names.Add("Sunny Chen");
            names.Add("Kitty Wang");
            names.Add("Sunny Crystal");
            List<string> found = names.FindAll
               (
               // Lambda Expression Implementation
               name => name.StartsWith(
                   "sunny",
                   StringComparison.OrdinalIgnoreCase)
               );
           if (found != null)
           {
               foreach (string str in found)
                   Console.WriteLine(str);
           }
       }
   }

上面的Lambda Expression Implementation在效果上与匿名方法没有任何区别,“=>”左边的name定义了参数(当参数个数为1的时候,圆括号可以省 略),“=>”右边定义执行体。由于C# 3.0编译器具有Type Inference的能力,参数类型与返回值都将由编译器通过上下文判定,因此与匿名方法不同,Lambda表达式的参数可以不给定参数类型。当所表示的 匿名方法没有任何参数时,Lambda表达式也同样可以使用,只需在“=>”左边用一对圆括号表示即可。即:

() => Console.WriteLine(“Hello!”);

事实上,“Lambda表达式”这一词比较笼统,事实上“=>”运算符既可以表示Lambda表达式,也可以表示Lambda语句。Lambda语句由代码块组成,形式上很像匿名方法。请看下面的例子:

    class Program
    {
         static void Main(string[] args)
         {
             // Lambda 表达式
             Func<int, bool> dele1 = n => n > 10;
             // Lambda 语句
             Func<int, bool> dele2 = (int n) => { return n > 10; };
             Console.WriteLine(dele1(16));
             Console.WriteLine(dele1(8));
         }
    }

两种定义方法同样可以正确地输出结果。请注意,当我们希望构建表达式树的时候,情况却完全不同了:

     // ok
    Expression<Func<int, bool>> expr1 = n => n > 10;
    // error: cannot converted to an expression tree
    Expression<Func<int, bool>> expr2 = (int n) => { return n > 10; };

由此可见,在构建表达式树的时候,不能用Lambda语句(带有代码语句的Lambda表达式),而应该使用Lambda表达式。从这里就可以看出匿名方法与Lambda表达式的区别了。

原文地址:http://www.cnblogs.com/daxnet/archive/2008/11/14/1687010.html

C#中IList与List区别

2011年8月28日 没有评论

首先,List<T>是一个类,IList<T>是一个接口。接口和类的区别是本质的,类是负责功能的实现,而接口则是负责功能的定义。所以它们的区别本质上也就是类和接口的区别。

具 体来说,IList 泛型接口是 ICollection 泛型接口的子代,并且是所有泛型列表的基接口。它仅仅是所有泛型类型的接口,并没有太多方法可以方便实用,如果仅仅是作为集合数据的承载体,那么使用 IList<T>完全可以胜任。但是更多的时候,我们要对集合数据进行处理,从中筛选数据或者排序。这个时候IList<T>就 爱莫能助了。

1、当你只想使用接口的方法时,IList<>这种方式比较好.他不获取实现这个接口的类的其他方法和字段,有效的节省空间.

2、IList <>是个接口,定义了一些操作方法这些方法要你自己去实现

List <>是泛型类,它已经实现了IList <>定义的那些方法

IList <Class1> IList11 =new List <Class1>();

List <Class1> List11 =new List <Class1>();

这两行代码,从操作上来看,实际上都是创建了一个List<Class1>对象的实例,也就是说,他们的操作没有区别。

只是用于保存这个操作的返回值变量类型不一样而已。

那么,我们可以这么理解,这两行代码的目的不一样。

List <Class1> List11 =new List <Class1>();

是想创建一个List<Class1>,而且需要使用到List<T>的功能,进行相关操作。

IList <Class1> IList11 =new List <Class1>();

只是想创建一个基于接口IList<Class1>的对象的实例,只是这个接口是由List<T>实现的。所以它只是希望使用到IList<T>接口规定的功能而已。

分类: .NET 标签: ,

Lambda表达式(Lambda Expressions)

2011年8月28日 1 条评论

先来个实例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace lambda
{
    class Author
    {
        public string Name { get; set;}
        public int NumberofArticle { get; set; }
    }

    class Program
    {
        #region Define delegate

        delegate void MyDelegate(string s);

        delegate int AddTwoNum(int x, int y);

        #endregion

        static void Main(string[] args)
        {
            myMethod(" and the parameter string.");

            Console.WriteLine();

            Console.WriteLine(addTwoInt(2, 4));

            List<Author> authors= new List<Author>()
            {
                new Author{Name="Jim", NumberofArticle=500},
                new Author { Name="jjj", NumberofArticle= 120},
                new Author{Name="dfdm", NumberofArticle=600},
                new Author { Name="ccccjj", NumberofArticle= 320}
            };

            //实现IEnumerable接口的类,可以支持foreach循环遍历对象的集合元素
            IEnumerable<Author> res = authors.Where((Author p) => p.NumberofArticle>500);

            foreach (Author r in res)
            {
                Console.WriteLine(r.Name);

            }

            Console.ReadKey();
        }

        static MyDelegate myMethod = s => { Console.WriteLine("Hello, my delegate! " + s); };

        static AddTwoNum addTwoInt = (x, y) => { return x + y; };
    }
}

Lambda 表达式(拉姆达表达式) 和 匿名方法 其实是一件事情。唯一的不同是:他们语法表现形式不同。Lambda 表达式是在语法方面的更进一步的进化。在本质上,他们是一件事情。他们的作用都是:产生方法。即:内联方法。

引用自 C#首席架构师Anders Hejlsberg 的原话:

http://www.ondotnet.com/pub/a/dotnet/2005/10/31/interview-with-anders-hejlsberg-part-2.html?page=2

lambda expressions and anonymous methods are really just two words for the same thing. The only thing that differs is, “What does the syntax look like?” And the lambda expressions are a further evolution of the syntax.But underneath, they do the same thing. They generate methods. You know, they’re in-line methods.

所以:我们要了解 Lambda 表达式 就应该同时也了解 匿名方法。下面先看一个简单的代码例子,分别用匿名方法和Lambda 表达式来实现对数组的搜索:

使用 .net 2.0 的匿名方法来搜索字符串数组中包含 a 的字符串数组

static void Main(string[] args)
{
string[] list = new string[] { “abc”, “12″, “java” };
string[] ll = Array.FindAll(list,
delegate(string s)
{
return s.IndexOf(“a”) >= 0;
}
);
foreach (string var in ll)
{
Console.WriteLine(var);
}
Console.ReadLine();
}

使用 .net 3.5 的Lambda表达式来搜索字符串数组中包含 a 的字符串数组

static void Main(string[] args)
{
string[] list = new string[] { “abc”, “12″, “java” };

string[] ll = Array.FindAll(list, s => (s.IndexOf(“a”) >= 0));
foreach (string var in ll)
{
Console.WriteLine(var);
}
Console.ReadLine();
}

从上述两个例子我们可以看出:

从代码书写角度,代码可读性角度来说:Lambda表达式 比匿名方法更简单了。

而 Lambda表达式 和 匿名方法都是干的同一件事情,让我们少写一个函数定义。函数的调用和函数的实现在一起完成了。

 

Lambda表达式的书写格式如下:

(参数列表) => 表达式或者语句块

其中:

参数个数:可以有多个参数,一个参数,或者无参数。

参数类型:可以隐式或者显式定义。

表达式或者语句块:这部分就是我们平常写函数的实现部分(函数体)。

 

一些Lambda表达式的书写范例:

 

有两个参数的 Lambda表达式例子:

注:别看比较复杂,LINQ中实际把 下述代码中的 delegate ,DoSomeThing 替你做了,所以你写代码的时候只需要写

var t = DoSomeThing<int>(7, 8, (x, y) => x * y); 这么一行。
public delegate T HongJunGuoTest01<T>(T t1, T t2);
class Program
{
private static T DoSomeThing<T>(T t1,T t2,HongJunGuoTest01<T> match)
{
return match(t1, t2);
}

static void Main(string[] args)
{
var t = DoSomeThing<int>(7, 8, (x, y) => x * y);
Console.WriteLine(t);
Console.ReadLine();
}
}

下面这些写法也是对的(你只需要修改Main函数中的代码,其他地方不需要动):

var t = DoSomeThing<int>(7, 8, (int x, int y) => x * y);

var t = DoSomeThing<string>(“7″, “8″, ( x,  y) => x + y);

或者我们写一个更复杂的: => 右边是一段语句块。

var t = DoSomeThing<int>(7, 8, (x, y) => { if (x < 5) { return (x + 8) * y; } else { return y; } });

 

最前面的例子是一个参数的例子,我们就不举一个参数的例子了,下面举一个没有参数的例子:

public delegate void HongJunGuoTest02();
class Program
{
private static void DoSomeThing(HongJunGuoTest02 match)
{
match();
}

static void Main(string[] args)
{
DoSomeThing(() => Console.WriteLine(“jajaja”));
Console.ReadLine();
}
}

函数式编程

匿名方法,Lambda表达式 都是函数式编程思想下的产物,

函数式编程如果从概念角度来理解,比较晦涩,简单来说:

函数式编程的主要特点就是没有变量,所有算法都用递归函数来书写。整个程序除了定义函数就是调用函数,没有其他类型的语句。

moq学习资源

2011年8月24日 没有评论
分类: 叽歪生活 标签: ,

如何使用MOQ进行单元测试

2011年8月24日 没有评论

一般情况下,一个单元测试应该被分割为如下四个步骤:

● 准备

● 搭建环境

● 构造被测对象

● 初始化被测对象

● 构造Mock对象

● 初始化Mock对象

● 连接被测对象和依赖项

● 声明期待

● 配置Mock(Mock<T>.Setup)对象以声明该Mock对象期待被怎样调用。

● 执行测试

● 调用被测对象的方法,完成测试步骤

● 校验测试结果

● 调用校验方法(Mock<T>.VerifyAll)对Mock对象上的期待动作进行校验。

● 使用Assert方法对被测对象的状态进行校验。

分类: 叽歪生活 标签: ,

Moq测试基础说谈(四)——Mock类,创建对象,实用工厂

2011年8月24日 没有评论

Mock<T> Class

定义为:

 

public class Mock<T> : Mock

where T : class

这的构造方法:

Mock<T>()

Mock<T>(MockBehavior)

Mock<T>(array<Object>[])

Mock<T>(MockBehavior, array<Object>[])

Mock的泛型实现类,它有很多方法和属性。这里一一列举。

(一)方法

(1)As<TInterface>方法

为mock添加接口实现(mock),可以给它指定设置。

在mock对象的属性(或方法)首次使用之前才有效。且,参数只能是接口。

定义:

public virtual Mock<TInterface> As<TInterface>()

where TInterface : class

 

示例:

两个接口:

其中的Icustomer接口还是前几篇中用到的,这里添加一个Iorder接口:

public interface IOrder

{

string ShowTitle(string str);

}

 

Mock测试:

var customer = new Mock<ICustomer>();

customer.Setup(p => p.GetCall()).Returns(“方法调用”);

customer.Object.GetCall();

var order=customer.As<IOrder>();

order.Setup(p => p.ShowTitle(It.IsAny<string>())).Returns(“ok”);

Assert.AreEqual(“ok”,order.Object.ShowTitle(“”));

 

这个将出现异常,因为在As之前,已经对GetCall进行了调用。

 

(2)SetUp方法

为模拟的对象中的方法指定设置,它有两个重载:

Setup(Expression<Action<T>>)

Setup<TResult>(Expression<Func<T,TResult>>)

 

从两个委托可以知道,这两个一个是为没有返回值的方法设置,一个是对有返回值的方法设置

public void TestSetUp()

{

var customer = new Mock<ICustomer>();

    customer.Setup(p => p.AddCall())

.Callback(()=>Console.WriteLine(“没有返回值”));

    customer.Setup(p => p.GetCall(It.IsAny<string>()))

.Returns(“ok”)

.Callback((string q) => Console.WriteLine(“有返回值”));

    customer.Object.AddCall();

    customer.Object.GetCall(“”);

}

这个前几篇已经广泛使用。

(3) 其它方法略

其它内容可见:

http://www.cnblogs.com/jams742003/archive/2010/03/02/1676585.html

现在看一下Mock的一个构造器:

Mock<T>(MockBehavior),定义:

public Mock(MockBehavior behavior)

 

MockBehavior类是自定义mock行为的枚举:

Strict mock对象没有合适的设置时,总是抛出异常

Loose 从不抛出异常,如果必要会返回默认值(引用类型为null,值类型为0,或枚举和数组为空)

Default mock行为默认值,就是Loose值

 

接口:

public interface IOrder

{

string ShowTitle(string str);

string ShowAddress();

}

 

测试:

public void TestBehivor()

{

    var order = new Mock<IOrder>();

    order.Object.ShowTitle(string.Empty);

}

 

这个接口有两个方法,在测试里,创建了mock对象,然后调用它的一个方法:ShowTitle

但在之前,这个方法并没有被设置,这时mock对象的默认行为为Default,也就是Loose,所以不会出现异常。

当把mock对象的行为设置为严格后:

var order = new Mock<IOrder>(MockBehavior.Strict);

order.Object.ShowTitle(string.Empty);

 

测试就会出错:IOrder.ShowTitle(“”) invocation failed with mock behavior Strict.

 

Mock的名字空间中还有一个MockFactory类,是一个用于构造多个mock对象的功能工厂

它有三个方法:

Create,Verify,VerifyAll

Create的方法有:

Create<T>()

Create<T>(MockBehavior)

Create<T>(array<Object>[])

Create<T>(MockBehavior,array<Object>[])

 

其中Create<T>(array<Object>[])定义:

public Mock<T> Create<T>(

    params Object[] args

) where T : class

 

它会根据参数来确定合适的被Mock的对象的构造器。

分类: 叽歪生活 标签:

Moq测试基础说谈(三)——属性,常用方法

2011年8月24日 没有评论

(一)属性

1测试

准备:

public class Customer

{

public virtual int Unid { get; set; }

public virtual string Name { get; set; }

}

测试

var customer = new Mock<Customer>();

customer.Setup(p => p.Name).Returns(“Tom”);

 

customer.SetupProperty(p => p.Name, “tt”);

以下就可以用这个属性了,和正常下属性访问相同。

二)Callbacks

当执行某方法时,调用其内部输入的(Action)委托

看它的5种重载:

Callback(Action)

Callback<T>(Action<T>)

Callback<T1, T2>(Action<T1, T2>)

Callback<T1, T2, T3>(Action<T1, T2, T3>)

Callback<T1, T2, T3, T4>(Action<T1, T2, T3, T4>)

这个方法调用其内部输入的Action委托,Aciton<T>有5种重载,所以这里的Callbacks有5种重载。

以第二个为例:

它的定义为:

ICallbackResult Callback<T>(Action<T> action)

这个表示一个输入参数,

var customer = new Mock<ICustomer>();

customer.Setup(p => p.GetCall(It.IsAny<string>()))

    .Returns(“方法调用”)

    .Callback((string s)=>Console.WriteLine(“ok”+s));

customer.Object.GetCall(“x”);

 

这个Callback要调用的是GetCall(string s)中的s参数。

三)Verify

用于测试mock对象的方法或属性是否被调用执行。当不需要测试结果时用到

重载很多:

Verify()

Verify(Expression<Action<T>>)

Verify<TResult>(Expression<Func<T, TResult>>)

Verify(Expression<Action<T>>, Times)

Verify(Expression<Action<T>>, String)

Verify<TResult>(Expression<Func<T, TResult>>, Times)

Verify<TResult>(Expression<Func<T, TResult>>, String)

Verify(Expression<Action<T>>, Times, String)

Verify<TResult>(Expression<Func<T, TResult>>, Times, String)

 

用其中三个举例

第一个:Verify(),定义为:

public void Verify()

测试

public void TestVerify()

{

    var customer = new Mock<ICustomer>();

    customer.Setup(p => p.GetCall(It.IsAny<string>()))

.Returns(“方法调用”)

.Callback((string s) => Console.WriteLine(“ok” + s))

.Verifiable();

    customer.Object.GetCall(“调用了!);

    customer.Verify();

}

把Mock对象中的GetCall方法设置为可证实的(Verifiable()),

如果不调用粗体部分语句,那么customer.Verify()执行就不能通过。这个方法很有用。

 

第二个:Verify(Expression<Action<T>>),定义为:

public void Verify(Expression<Action<T>> expression)

 

customer.Verify(p => p.GetCall(“call”));

如果没有调用且输入call字串的参数,则失败。

第三个:Verify(Expression<Action<T>>, Times, String)

定义:

public void Verify(  Expression<Action<T>> expression,

    Times times,

    string failMessage

)

 

这个比上一个多了两个参数,一个用于表示调用次数相关描述,一个用于失败的时打印信息

customer.Setup(p => p.GetCall(It.IsAny<string>()))

                .Returns(“方法调用”)

                .Callback((string s) => Console.WriteLine(“ok” + s))

                .Verifiable();

customer.Object.GetCall(“call”);

customer.Object.GetCall(“call”);

customer.Verify(p => p.GetCall(“call”),

Times.AtLeast(2),”至少应被调用2次”);

 

当GetCall方法被调用最少2次(且参数为call)时,测试成功。

方法中Times是个Times类类型,它有多个方法:

AtLeast

AtLeastOnce

AtMost

AtMostOnce

Between

Exactly

Never

Once

可以从语义上理解它们各自是什么意思,例如:AtLeast的定义为:

public static Times AtLeast(

    int callCount

)

四)VerifyAll

在使用Verify方法时,只有被标记为可证实的(.Verifiable())的才可以验证。

但VerifyAll会验证所有的调用:

 

customer.Setup(p => p.GetCall(It.IsAny<string>()))

         .Returns(“方法调用”)

         .Callback((string s) => Console.WriteLine(“ok” + s));

customer.Object.GetCall(“call”);

customer.VerifyAll();

原文地址:http://www.cnblogs.com/jams742003/archive/2010/03/02/1676585.html

分类: 叽歪生活 标签:

Moq测试基础说谈(二)——Mock方法,方法参数

2011年8月24日 没有评论

准备工作:

public interface ICustomer

{ }

(一)方法

(1)普通的方法

在接口中添加3个方法:
void AddCall();

string GetCall();

string GetCall(string strUser);

 

Mock测试:

var customer = new Mock<ICustomer>();

customer.Setup(p=>p.AddCall());

customer.Setup(p => p.GetCall()).Returns(“phone:89898789″);

customer.Setup(p => p.GetCall(“Tom”)).Returns(“Hello”);

customer.Object.AddCall();

Assert.AreEqual(“phone:89898789″, customer.Object.GetCall());

Assert.AreEqual(“Hello”, customer.Object.GetCall(“Tom”));

 

先是建立Icustomer接口的mock对象:customer,这个对象执行Icustomer接口的业务。

然后对接口中的三个方法进行依赖添加:

这里只说第三个方法:

customer.Setup(p => p.GetCall(“Tom”)).Returns(“Hello”);

customer对象的GetCall方法,传递参数Tom,得到返回值:Hello,相当于:

string GetCall(“Tom”){return “Hello”;}

 

测试,当调用无参数的GetCall()时,已经得知它会返回:phone:89898789

当调用有参数的GetCall(string)时,如果参数是”Tom”,那么返回:Hello

(2)带有引用或输出参数的方法

string GetAddress(string strUser, out string Address);

string GetFamilyCall(ref string strUser);

 

var customer = new Mock<ICustomer>();

var outString=”oo”;

customer.Setup(p => p.GetAddress(“”, out outString)).Returns(“shijiazhuang”);

customer.Setup(p => p.GetFamilyCall(ref outString)).Returns(“xx”);

(3)有返回值的普通方法

还用string GetCall(string strUser);

这个进行测试,这里可以操作参数:

var customer = new Mock<ICustomer>();

customer.Setup(p => p.GetCall(It.IsAny<string>()))

.Returns((string s) => “Hello “+s);

Assert.AreEqual(“Hello Tom”,customer.Object.GetCall(“Tom”));

GetCall方法返回值是:Hello+调用参数

(4)调用方法时抛出异常

方法:void ShowException(string str);

测试:

var customer = new Mock<ICustomer>();

customer.Setup(p => p.ShowException(string.Empty))

.Throws(new Exception(“参数不能为空!”));

customer.Object.ShowException(“”);

 

如果传入的参数是空值(string.Empty),那么调用这个方法时(Mock调用)就会触发异常。

(5)调用时赋值

方法:void AddCall();

Mock测试:

var customer = new Mock<ICustomer>();

int iCount = 0;

customer.Setup(p => p.AddCall()).Callback(()=>iCount++);

Assert.AreEqual(0, iCount);

customer.Object.AddCall();

Assert.AreEqual(1, iCount);

customer.Object.AddCall();

Assert.AreEqual(2, iCount);

customer.Object.AddCall();

Assert.AreEqual(3, iCount);

 

 

(二)匹配参数

var customer = new Mock<ICustomer>();

customer.Setup(p => p.SelfMatch(It.IsAny<int>()))

.Returns((int k) => “任何数:” + k);

Console.WriteLine(customer.Object.SelfMatch(100));

customer.Setup(p => p.SelfMatch(It.Is<int>(i => i % 2 == 0)))

.Returns(“偶数”);

Console.WriteLine(customer.Object.SelfMatch(100));

customer.Setup(p => p.SelfMatch(It.IsInRange<int>(0, 10, Range.Inclusive)))

.Returns(“10以内的数”);

Console.WriteLine(customer.Object.SelfMatch(8));

customer.Setup(p => p.ShowException(It.IsRegex(@”^\d+$”)))

.Throws(new Exception(“不能是数字”));

customer.Object.ShowException(“r4″);

 

It用于添加参数约束,它有以下几个方法:

Is<T>:匹配确定的给定类型

IsAny<T>:匹配给定的任何值

IsInRange<T>:匹配给定类型的范围

IsRegex<T>:正则匹配

 

通过示例来演示一下:

(1)Is<T>

customer.Setup(x => x.SelfMatch(It.Is<int>(i => i % 2 == 0))).Returns(“1″);

 

方法SelfMatch接受int型参数,当参数为偶数时,才返回字符串1。

i=>i%2==0这个表达式的意思在以前的随笔中已经解释过,详细可见:

http://www.cnblogs.com/jams742003/archive/2009/12/23/1630737.html

(2)IsAny<T>

customer.Setup(p => p.SelfMatch(It.IsAny<int>())).Returns((int k) => “任何数:” + k);

 

方法SelfMatch接受int型,且任何int型参数都可以,然后返回:”任何数:” + k。

这里说明一下Returns方法:

 

Returns(Func<TResult>)

Returns<T>(Func<T,TResult>)

Returns<T1,T2>(Func<T1,T2,TResult>)

Returns<T1,T2,T3>(Func<T1,T2,T3,TResult>)

Returns<T1,T2,T3,T4>(Func<T1,T2,T3,T4,TResult>)

Returns(TResult)

 

在这个例子中,用到的就是第一种重载,关于Func委托,可以见我的:

http://www.cnblogs.com/jams742003/archive/2009/10/31/1593393.html

因为Func最多接受4个传入参数(有5个重载),所以这里的Returns带有Func委托参数的重载也有5个。

(3)IsInRange<T>

customer.Setup(p => p.SelfMatch(It.IsInRange<int>(0, 10, Range.Inclusive)))

.Returns(“10以内的数”);

方法SelfMatch接受int型,且当范围在[0,10]时,才返回10以内的数

其中,这个方法,带有一个包含与排除开关。

(4)IsRegex<T>

customer.Setup(p => p.ShowException(It.IsRegex(@”^\d+$”)))

.Throws(new Exception(“不能是数字”));

原文地址:http://www.cnblogs.com/jams742003/archive/2010/03/02/1676197.html

分类: 叽歪生活 标签: , ,

Moq测试基础说谈(一)——简介,下载

2011年8月24日 没有评论

Moq,就是Mock you。读音可以读成Mock~you。是Mock框架的一种。用于测试中的Mock测试。Mock是模拟的意思。Mock是模拟对象的一种技术。

它可以用于以下情况(引用):

—– 真实对象具有不可确定的行为(产生不可预测的结果,如股票的行情)

—– 真实对象很难被创建(比如具体的web容器)

—– 真实对象的某些行为很难触发(比如网络错误)

—– 真实情况令程序的运行速度很慢

—– 真实对象有用户界面

—– 测试需要询问真实对象它是如何被调用的(比如测试可能需要验证某个回调函数是否被调用了)

—– 真实对象实际上并不存在(当需要和其他开发小组,或者新的硬件系统打交道的时候,这是一个普遍的问题)

 

举个明了的例子:在开发一套BS网店系统时,想集中精力开发业务逻辑部分,而不想在数据层上花费太多时间,这时,可以通过Mock对象来模拟数据层,而不必去为数据连接,CRUD,Mapping等等去做太多的事,而又可以使业务测试可以进行下去。

下载地址:

http://code.google.com/p/moq/

这里有一些文档说明。

可以模拟接口和存在的类。在模拟类时有一些限制。类不能是密封的。方法要加上虚修饰符。不能模拟静态方法(可以通过适配器模式来模拟静态方法)。

下边是一个小例子

准备工作:

public interface ITaxCalculator

{

decimal GetTax(decimal rawPrice);

}

public class Product

{

public int ID { get; set; }

public String Name { get; set; }

public decimal RawPrice { get; set; }

public decimal GetPriceWithTax(ITaxCalculator calculator)

{

         return calculator.GetTax(RawPrice) + RawPrice;

}

}

测试

public void TestTax()

{

    Product myProduct = new Product { ID = 1, Name = “TV”, RawPrice = 25.0M };

    Mock<ITaxCalculator> fakeTaxCalculator = new Mock<ITaxCalculator>();

    fakeTaxCalculator.Setup(tax => tax.GetTax(25.0M)).Returns(5.0M);

    decimal calculatedTax = myProduct.GetPriceWithTax(fakeTaxCalculator.Object);

    fakeTaxCalculator.Verify(tax => tax.GetTax(25.0M));

    Assert.AreEqual(calculatedTax, 30.0M);

}

其中:

Mock<ITaxCalculator> fakeTaxCalculator = new Mock<ITaxCalculator>();

fakeTaxCalculator.Setup(tax => tax.GetTax(25.0M)).Returns(5.0M);

这部分就是建立Mock对象。

这里其实对GetTax方法进行了模拟:

GetTax(25.0M){return 5.0M;}

当调用myProduct.GetPriceWithTax(fakeTaxCalculator.Object)时,那么,

return calculator.GetTax(RawPrice) + RawPrice;

现在calculator对象已经进行了模拟,GetPriceWithTax返回GetTax的值+RawPrice的值。

此时的Product的RawPrice的值为25.0M,从这个值可以得到tax.GetTax(25.0M)的值是5.0M。而25.0M+5.0M的值是30.0M。所以返回的值是30.0M。这个断言是正确的。

原文地址:http://www.cnblogs.com/jams742003/archive/2010/03/02/1676215.html

分类: 叽歪生活 标签: , ,

依赖注入容器Unity Application Block快速入门

2011年8月15日 没有评论

Unity是微软模式与实践团队开发的一个轻量级、可扩展的依赖注入容器,之前我也有过一篇文章《Enterprise Library 4.0中的依赖注入容器(Unity)预览》对其做过介绍。微软模式与时间团队已经在2月份发布了Unity February 2008 CTP版本,官方主页是:http://www.codeplex.com/unity,大家可以到网站上去下载相关的源代码。本文将通过一些示例让您对Unity使用有一个逐步的认识和了解。

准备相关代码

为了接下来的说明,我们先编写几个后面需要的接口和类:

接口ILogger

public interface ILogger
{
    void Write(string message);
}

FlatFileLogger类

public class FlatFileLogger : ILogger
{
    public void Write(string message)
    {
        Console.WriteLine(String.Format("Message:{0}", message));
        Console.WriteLine("Target:FlatFile");
    }
}

DatabaseLogger类

public class DatabaseLogger : ILogger
{
    public void Write(string message)
    {
        Console.WriteLine(String.Format("Message:{0}",message));
        Console.WriteLine("Target:Database");
    }
}

创建容器

在Unity中创建容器实例有两种方法,一是直接使用构造函数创建,如下代码所示:

class Program
{
    static void Main(string[] args)
    {
        IUnityContainer container = new UnityContainer();
    }
}

第二种通过父容器创建容器,在Unity中提供了层级容器的创建,即可以通过父容器来逐级创建容器:

通过父容器来创建容器可以使用CreateChildContainer:

class Program
{
    static void Main(string[] args)
    {
        UnityContainer parentContainer = new UnityContainer();

        UnityContainer childContainer = parentContainer.CreateChildContainer();
    }
}

采用层级容器的好处是我们可以对于有不同生命周期的对象放在不同的容器中,如果一个子容器被释放,不会影响到其它子容器中的对象,但是如果根节点处父容器释放后,所有的子容器都将被释放。

class Program
{
    static void Main(string[] args)
    {
        UnityContainer parentContainer = new UnityContainer();

        UnityContainer childContainer = parentContainer.CreateChildContainer();

        // can use both generated objects here

        // Dispose child container
        childContainer.Dispose();

        // can use only object in parent container here

        // Dispose parent container
        parentContainer.Dispose();
    }
}

注册接口映射

在Unity中提供了Register方法供我们在容器中注册接口映射:

class Program
{
    static void Main(string[] args)
    {
        IUnityContainer container = new UnityContainer();

        container.Register<ILogger, DatabaseLogger>();
        container.Register<ILogger, FlatFileLogger>("flatfileLogger");
    }
}

第一个泛型参数是基类型,第二个泛型参数是组件类型,它们之间必须满足一定的泛型约束关系:

IUnityContainer Register<TFrom, TTo>() where TTo : TFrom;

另外,如果在注册组件时没有指定我们也可以使用非泛型的Register方法进行接口映射:

class Program
{
    static void Main(string[] args)
    {
        IUnityContainer container = new UnityContainer();
        container.Register(typeof(ILogger),typeof(DatabaseLogger));
        container.Register(typeof(ILogger),typeof(FlatFileLogger),"flatfileLogger");
    }
}

获取对象实例

在Unity中提供了Get方法用以获取对象实例,如下代码所示:

class Program
{
    static void Main(string[] args)
    {
        IUnityContainer container = new UnityContainer();

        container.Register<ILogger, FlatFileLogger>();

        ILogger logger =  container.Get<ILogger>();

        logger.Write("TerryLee");
    }
}

在上面代码中,我们在容器中注册了一个ILogger接口到FlatFileLogger的映射,当调用Get<ILogger>()方法时,将返回ILogger的一个默认的类型实例。运行代码可以看到:

在之前注册接口映射部分,Register方法有一个重载是为接口映射指定一个特定的名称,这样我们可以根据名称和接口来获取一个特定类型的对象实 例,如下面的代码我们同时注册FlatFileLogger和DatabaseLogger到接口ILogger的映射,并未 DatabaseLogger指定一个名称,在使用Get方法的时候就可以通过ILogger和指定的名称来获取DatabaseLogger实例:

class Program
{
    static void Main(string[] args)
    {
        IUnityContainer container = new UnityContainer();

        container.Register<ILogger, FlatFileLogger>();
        container.Register<ILogger, DatabaseLogger>("databaseLogger");

        ILogger logger = container.Get<ILogger>("databaseLogger");

        logger.Write("TerryLee");
    }
}

再运行代码,可以看到获取到了DatabaseLogger实例:

如果我们同时同时注册FlatFileLogger和DatabaseLogger到接口ILogger的映射,并且不指定任何名称,那么直接调用Get方法将会返回后注册的组件的对象实例,如下面的代码:

class Program
{
    static void Main(string[] args)
    {
        IUnityContainer container = new UnityContainer();

        container.Register<ILogger, FlatFileLogger>();
        container.Register<ILogger, DatabaseLogger>();

        ILogger logger = container.Get<ILogger>();

        logger.Write("TerryLee");
    }
}

运行后会返回DatabaseLogger实例:

当然我们也可以使用非泛型的Get方法来获取对象实例:

class Program
{
    static void Main(string[] args)
    {
        IUnityContainer container = new UnityContainer();

        container.Register<ILogger, FlatFileLogger>();

        ILogger logger = container.Get(typeof(ILogger)) as ILogger;

        logger.Write("TerryLee");
    }
}

获取所有对象实例

除了可以获取单个对象实例之外,我们还可以一次获取容器中所有与某一接口映射的所有对象实例,但是需要依赖于在注册映射时提供的名称,如果没有指定名称,通过GetAll方法不会被获取到。

class Program
{
    static void Main(string[] args)
    {
        IUnityContainer container = new UnityContainer();

        container.Register<ILogger, FlatFileLogger>();
        container.Register<ILogger, FlatFileLogger>("flatFileLogger");
        container.Register<ILogger, DatabaseLogger>("DatabaseLogger");
        IEnumerable<ILogger> loggers = container.GetAll<ILogger>();

        foreach (ILogger logger in loggers)
        {
            if (null != logger)
            {
                Console.WriteLine(logger.GetType().ToString());
            }
        }
    }
}

运行后如下,第一个没有提供名称的类型实例不会被获取到:

结束语

关于Unity的一个简单使用就到这里了,希望能够对大家有用。使用Unity简单来说分为创建容器、注册接口映射、获取对象实例这样三步,在下篇文章中,将会说到如何使用配置文件注册接口映射以及如何注册Singleton实例等内容。

作者:TerryLee
出处:http://terrylee.cnblogs.com

分类: .NET 标签: ,

开发者何去何从?盛大九城网易腾讯开放平台比较

2011年8月5日 没有评论

自从2007年苹果和Android的开放平台相继问世,为第三方开发者提供了一个门槛更低的出头之路。在这两大平台持续火爆的情况下,越来越多的 公司推出自己的开放平台;在一系列成功个案的传播之下,越来越多的个体、团队开发者投入到开放平台开发之中,迈出创业的第一步。

纵观各个开放平台的应用销售情况,游戏都是最受用户欢迎和消费最多的应用。而在国内,由于盗版、用户使用和消费习惯等原因,游戏开放更毋庸置疑地成为第三方投身开放平台的首选。现时国内的游戏开放平台数量众多,特点各不相同。

一、盛大麻球平台

平台类型:Web

游戏类型:Flash

获利方式:内置广告和道具收入分成,额度50%

平台已有游戏:40000多款

分析:作为在中国最早推出的第三方游戏开发平台,麻球是盛大在美国收购的游戏分销和内置广告平台Mochimedia。平台除了经营自有社区,还以 内容提供方的身份对外合作,以Flash小游戏为内容,以IGA为主要获利手段。由于是外购的成熟平台,游戏发布渠道广泛、已有用户基数大是平台的优势, 同时由于成熟平台,平台中已有游戏数量众多,达到40000多款,竞争十分激烈,据了解大部分小游戏收入相当少,想要获得大量收入的难度相当大。而 Flash小游戏在制作门槛上相对较低,个体开发者可以独自完成开发。

二、九城无线社区平台

平台类型:移动平台

游戏类型:各类规模移动客户端游戏

获利方式:销售收入分成,提供保底分成方式

平台已有游戏:4500款

分析:与盛大麻球一样,九城无线社区也是九城在通过参股美国移动平台OpenFeint从而引进国内的开发平台。与麻球相同的是该平台以提供小游戏 为主,不同的是平台是基于移动通信工具的平台。在国外,该平台以销售单机游戏为主要收入,目前已有6500万注册用户以及4500款游戏,用户众多但是竞 争也激烈。该平台特点与麻球较为相似,技术门槛低而收入也相对较低,国内用户购买正版游戏的消费习惯还未形成,前景较难预测。

三、网易iTown平台(iTownSDK)

平台类型:PC有端网络游戏平台

游戏类型:网络休闲游戏为主

获利方式:5万以上上线奖金,80%利润分成,在线人数奖励

平台已有游戏:目前平台未上线

分析:网易iTown平台是PC有端网络游戏平台,游戏主要以道具收入为主。在国内用户对客户端网络游戏的道具消费方式已经形成习惯,而且除了 2:8利润分成,网易还提供5万元以上上线奖金和在线人数奖励。客户端网络游戏开发有一定的技术门槛,个体开发者难以独自完成游戏开发,比较适合团队开 发。而网易还提供sdk以帮助快速开发,并配以专门的技术人员解答开发问题。iTown平台并未对外测试,所以现时并没有用户基础,游戏数量因开发门槛也 不会像麻球、OpenFeint般的数量级,因此竞争并不激烈,对开发者来说是既是风险也是机会。

四、腾讯开放平台

平台类型:Web为主

游戏类型:QQ互联和应用接入

获利方式:收入分成

平台已有游戏:76款,其他应用168款。此外,近2万个合作伙伴已经或正在排队等待接入。

分析:坐拥数亿结成关系用户的腾讯将旗下平台开放,对第三方来说是一件天大的好事,其重要程度就像是第二个 Facebook开放一样。不过像腾讯这样的优质平台,必定会吸引众多有实力的大公司前来加入,国外社交游戏巨头Zygna日前就已与腾讯达成合作,上线 其拳头产品之一《CityVille》;而以腾讯对用户资源的谨慎以及对获利的要求来说,其合作对象、资源和服务必定倾向于有实力的大公司。根据知情人士 披露,在开放的同时,腾讯也在酝酿推出一个类似“竞价排名”的广告系统,为愿意付出更多现金的开发者提供推广通道,而如果这一广告系统太过注重盈利性的 话,更加会使用户更多流入资金实力雄厚的公司手中。就像Facebook最终被几大游戏公司寡头垄断一样,腾讯平台也必定被大公司占据。

结语:国内其他开放平台,如人人网、新浪微博等平台,其特点可以归类到以上几个平台中,在此不再逐一分析。综合以上对几个主要开放平台的分析,可以 知道不同的开发者应该根据平台特点和自身情况,选择合适自己的平台进行开发。目前国内开发者和团队大多是拥有一份正职,以兼职的形式开发,希望逐步积累经 验和资金以达到创业的目的。

个体开发者在Appstore中大获成功只是沧海一粟又或是被包装出来的个案,如果实力不够而还要盲目投向优质资源的大平台,直面惨烈的竞争,最终只会成为大公司的炮灰。开发者只有抱团组队,选择好的创业起步平台,才能提高成功的可能性。

网易iTown游戏平台:开放的不只是接口

2011年8月5日 2 条评论

8月5日消息,网易iTown游戏平台发起的“iTwonSDK百万奖金大赛”开启1月有余,而一系列的现象表明,网易的这个第三方游戏平台已开始 颠覆原有的第三方开发团队的开发方式。“以前我们为其他游戏平台做开发时,既要设计聊天室、又要构架很多基础功能,这些占据了我们大量的时间和精力,而为 了降低开发成本,让游戏尽快上线,我们只能缩减游戏玩法方面的内容,而为iTown做开发,我们只需要做好一件事情—设计一个有创意的玩 法。”Guudo game游戏CEO沈伟华如此表示。

图1 iTownSDK

2011年互联网行业最流行、最风靡的一个词,一定是“开放”,而不同于腾讯、人人、新浪等互联网巨头声势浩大的开放宣言,作为国内游戏行业的巨 头,有着多年自主研发经验的网易,却悄悄的开启了自己的开放之路,从一定意义上讲,这也是网易CEO丁磊的一贯做法,低调、务实。而不同于其他公司仅仅只 是打开接口的方式,网易的iTown游戏平台提供了一系列的开发工具和网易多年的游戏开发资源,用网易游戏副总裁丁迎峰的话来说:“我们不仅仅是开放一个 接口或是一个平台给开发团队,我们开放的是网易多年游戏研发的经验和方式,开放的是一个保证开发团队能做出绝妙游戏的环境。”

重金3年打造iTown游戏平台

如果说开放这个词是今年才在中国开始风靡,但是在网易,3年前便开始了酝酿和准备。“当时仅仅只是从一个游戏人的角度出发,想跟更多的游戏爱好者一 起设计出更多的游戏,我们希望能看到更多的游戏模式、更多的游戏玩法、更有创意的游戏设计。因此,我们开始着手进行iTown游戏平台的筹备,前身也叫 IGATE游戏开发者计划。”网易游戏副总裁丁迎峰谈到为何3年前便开始了iTown的筹备时如是表示。

而在筹备的这三年中,随着大环境的变化,随着互联网越来越开放,将iTown游戏平台以一个更加开放更加完善的方式呈现给第三方游戏开发团队也成了 理所当然的转变。在这个转变中,却仍然保留了最核心的一个理念:让开发者更加专注于游戏核心玩法设计。正是因为这个保留,也使得iTown游戏平台跟其他 平台明显的区隔出来,并开始颠覆原有的三方开发团队的开放方式。“相比较与以前为appstore做开发,我们的策划和程序可以将所有的精力放在游戏核心 玩法的设计上,我们只需要去思考游戏怎么才好玩,应该怎么去设置更多的关卡,而这也保证了我们的游戏在上市时能以一个十分高的品质面向玩家。”Guudo game游戏CEO沈伟华表示对于这样的转变,十分适合像Guudo game这样规模不大但却充满了创意才华的中小型开发团队。

角色互换、颠覆传统第三方开发模式

相对于其他开放平台中平台商的裁判角色,网易在开放iTown游戏平台时,更多的在扮演一个良师的角色,以一个有着十几年开发经验的资深游戏玩家的 角度,帮助第三方团队进行游戏测评,并提供改进意见,帮助解决各类技术问题,这不仅仅可以帮助开发者更加专注于核心玩法开发,也营造了一个良好的、共同成 长的开发环境,而这些也吸引着越来越多的开发团队加入iTown游戏平台。这种平台商解决基础功能、第三方团队专注游戏玩法;平台商提供测评和技术支持, 第三方团队完善游戏核心玩法的新模式,从根本上改变了开发团队孤军作战,游戏设计牵涉多个环节,无法全力投入游戏玩法设计的传统模式弊端。而后期利润 2:8(网易:开发团队)分成的模式,也保证了高质量的产品一定能获得丰厚的回报。

iTown究竟要实现一个怎样的目标?网易iTown游戏平台产品经理岑晋宇如此表示:“我们希望随着iTown游戏平台开放的逐步深入,我们的这 种模式、这种努力能带来更多的高质量的游戏;希望很多充满创意的游戏开发团队能够加入到其中来。只要你有创意,你就有机会带来一款无与伦比的完美游戏。”

分类: 叽歪生活 标签: ,

CouchDB与SQLite两创始人联手开发NoSQL通用查询语言UnQL

2011年7月31日 没有评论

H-Online网站报道,知名NoSQL项目CouchDB的创始人(也是CouchBase公司的CTO)Damien Katz与著名开源嵌入式数据库项目SQLite的创始人Richard Hipp最近在共同从事一项重要的工作:为NoSQL制定一个通用的查询语言标准,扫除业界采纳这些新技术的大障碍。

这种新的语言名为UnQLUnstructured Query Language/非结构化查询语言的简称,读如“Uncle”),在很多方面都很像SQL,也有SELECTINSERTUPDATEDELETE等命令,但 是与SQL主要用来处理关系数据库中的表不同,UnQL处理的对象是文档数据库中的无序非结构化文档的集合(称为Collection)。在UnQL中, 文档就是可以用JSON描述的数据单位,但无需是JSON对象,也可以是整数、浮点数、字符串。与传统关系型数据库不同,Collection可以保护多 个结构不同的文档。UnQL中与CREATE TABLE和DROP TABLE命令对应的是CREATE COLLECTION和DROP COLLECTION。查询的WHERE子句指向的是与所存对象的字段对应的文档属性。

历史上,SQL语言的出现为关系型数据库得到广泛应用立下了汗马功劳。Katz相信统一的NoSQL查询语言也会产生类似的作用。而Hipp则表示,UnQL将基于他们丰富的SQL经验,在SQL之外补充适于后现代应用软件中非结构化、自描述数据格式的语法和概念。

目前语言标准的草稿已经可以在网站unqlspec.org上看到,但很不完整,只有语法树简单的语法说明,实例和代码都没有。

分类: 数据库 标签: ,

ASP.NET异常处理准则

2011年7月30日 5 条评论

在开发应用程序的时候,异常处理是非常的重要的,我找到一些异常处理准则,将它共享出来,如有不同意见,欢迎提出来一起探讨。

1、决不“catch”一个Exception,却什么也不处理。如果您隐藏了异常,你永远不会知道是否发生过异常。

2、如果产生Exception,给用户提供一个友好的信息,但记录与有关错误的所有可能的细节,包括它的发生时间,方法和类的名字等实际的错误信息。

3、始终捕获特定的异常,而不是一般的异常和系统异常。

4、你可以有一个应用程序级(线程级)的错误处理程序,您可以用它处理所有一般异常。在一个’意外一般错误”中,这个错误处理程序应该捕获该异常并记录他,除此之外,在应用程序关闭之前应该做出友好的信息提示或者允许用户选择忽略异常继续。

5、不要在所有的方法中写的try – catch。只在有可能有某个特定的异常发生的方法中使用它。例如,如果你是一个文件读写操作,只需处理FileIOException。

6、不写非常大的try – catch块。如果需要,为您执行每个任务编写单独的try – catch程序,在try – catch中只有一段特定的代码。这将帮助你找到哪一段代码产生的异常,你可以给特定的错误信息给用户。

7、如果在你的应用需要,你可以写自己的自定义异常类。自定义异常不要继承SystemException基类。相反,继承ApplicationException。

8、当发生异常时,为了确保清理占据的资源,使用try / finally块。在finally子句中关闭的资源。使用try / finally块,即使发生异常,也能确保资源disposed。

9、错误消息应能帮助用户解决问题。不要给出例如”应用程序发生异常”, “有一个错误”等信息,相反要给出具体的信息“无法更新数据库。请确保登录ID和密码是正确的。”

10、当显示错误信息时,除了告诉什么是错误,该消息还应该告诉用户,不是“无法更新数据库。”,而是建议用户应怎样做:“无法更新数据库。请确保登录ID和密码是正确的。”

11、显示简短和友好的信息给用户。但要尽一切可能的记录实际的错误日志信息。这将有助于诊断问题。

12、在Global.asax中定义一个全局错误处理程序来捕获任何未在代码中处理异常。你应该在事件日志中记录所有的异常追踪和随后进行分析。

 

原文:http://hi.baidu.com/479775812/blog/item/76e4fdadc2ba58054b36d616.html

分类: 叽歪生活 标签:

asp.net三层架构异常处理

2011年7月30日 没有评论

异常可以分为系统异常和业务异常,业务异常必须被转化为业务执行的结果
DataAccess层不得向上层隐藏任何异常。
要明确区分业务执行的结果和系统异常。比如验证用户的合法性,如果对应的用户ID不存在,不应该抛出异常,而是返回(或通过out参数)一个表示验证结果的枚举值,这属于业务执行的结果。但是,如果在从数据库中提取用户信息时,数据库连接突然断开,则应该抛出系统异常。
BL层应根据业务的需要捕获某些系统异常,并将其转化为业务执行的结果。
UI层除了从调用BL层的API获取的返回值来查看业务的执行结果外,还需要截获所有的系统异常,并将其解释为友好的错误信息呈现给用户。
一般使用log4net实现日志记录和自定义异常处理。

更多的讨论:

http://topic.csdn.net/u/20090531/10/75a5a96a-f012-491f-9e7d-a4e9c4c17fc4.html