通用扩展
通用扩展的类名为 GenericExtension
。这里提供了比较通用的扩展方法,大多是基于 System.Object
类型的扩展。
1、判断对象是否为空
IsNullOrEmpty 方法可以判断任意类型的对象是否为空。它对对象进行null、DBNull,或是空字符串检测,如果是可枚举类型则检查有没有元素。
[TestMethod]
public void TestIsNullOrEmpty()
{
var list = new List<string>();
object obj = null;
int? i = null;
Assert.IsTrue(list.IsNullOrEmpty());
Assert.IsTrue(obj.IsNullOrEmpty());
Assert.IsTrue(i.IsNullOrEmpty());
}
2、尝试销毁对象资源
TryDispose 方法会判断实例是否实现了 IDisposable
接口,如果实现则调用 Dispose 方法来释放其使用的资源。
💡 扩展阅读
Fireasy 里提供了一个 DisposableBase
类,它实现了标准的 IDispose
模式。TryDispose 方法还有一个参数: disposing,一般在一个类重写 Dispose (bool disposing) 方法时,会传递该参数,它会告诉对象是通过 Dispose 方法调用还是通过析构函数调用。
protected bool Dispose(bool disposing)
{
db.TryDispose(disposing);
}
3、判断对象是否为空,非空时操作对象
AssertNotNull 方法用于判断对象是否为空,如果不为空,则返回对象的属性,或调用方法。
比如判断可空的日期类型,并输出字符串:
[TestMethod]
public void TestAssertNotNull()
{
DateTime? date1 = null;
DateTime? date2 = DateTime.Parse("2009-1-1");
Assert.IsNull(date1.AssertNotNull(s => s.ToShortDateString()));
Assert.AreEqual("2009-01-01", date2.AssertNotNull(s => s.ToString("yyyy-MM-dd")));
}
注:在C#6语法中,可以使用?来进行判断,所实现的效果和以上代码是一致的,如:
[TestMethod]
public void TestAssertNotNull()
{
DateTime? date1 = null;
DateTime? date2 = DateTime.Parse("2009-1-1");
Assert.IsNull(date1?.ToShortDateString());
Assert.AreEqual("2009-01-01", date2?.ToString("yyyy-MM-dd"));
}
4、As 和 Is
As 和 Is 方法判断实例是否可转换为指定的类型。As 和 Is 方法的效果与 as 和 is 关键字的效果一样,只是 As 方法可以在结果为 true 的情况下,通过委托调用其方法。
public interface IInvokable
{
void Invoke();
}
public class GenericInvoker : IInvokable
{
public void Invoke()
{
Console.WriteLine("hello world");
}
}
public class NoGenericInvoker
{
}
[TestMethod]
public void TestAs()
{
var obj1 = new GenericInvoker();
obj1.As<IInvokable>(s => s.Invoke());
var obj2 = new NoGenericInvoker();
obj2.As<IInvokable>(s => s.Invoke(), () => Console.WriteLine("obj2不是 IInvokable 类型"));
}
注:在C#7语法中,可以使用 is 操作符来判断变量是否成立,并转换成对应类型的变量,如:
[TestMethod]
public void TestIs()
{
var obj1 = new GenericInvoker();
if (obj1 is IInvokable invoker) { invoker.Invoke(); }
}
5、安全的ToString
一般地,调用 ToString 方法时,如果对象为空,则会引发 “未将对象引用设置到对象的实例”
这样的异常。ToStringSafely 方法会判断对象是否为空,不为空才调用 ToString 方法。
public class GenericData
{
public override string ToString()
{
return string.Empty;
}
}
[TestMethod]
public void TestToString()
{
GenericData data = null;
//使用ToString()将抛出异常
Assert.AreEqual(string.Empty, data.ToStringSafely());
}
6、转换类型
To 方法不像调用 Convert.ChangeType 方法那么简单地处理值类型,它加入了一些智能的判断,比如可以将 1、0、"True"、"False" 等转换为布尔类型,可支持枚举类型与数字、字符串之间的转换。如果源对象为空,可以返回指定的默认值。
[TestMethod]
public void TestTo()
{
Assert.AreEqual(true, "true".To<bool>());
Assert.AreEqual(false, "0".To<bool>());
Assert.AreEqual(2332.4m, "2332.4".To<decimal>());
Assert.AreEqual(34, "34d".To<int>(34));
}
对于原生类型,如 List、Dictionary、Dynamic,To 方法可以将对象转换为其他的类型,它在目标类型中寻找相同的属性进行复制。即使转换的类型是接口,它也会动态编译一个类型来实现该接口,以便能够最终转换。
public interface IPeople
{
string Name { get; set; }
}
[TestMethod]
public void TestObjectTo()
{
var source = new List<object>();
source.Add(new { Name = "huangxd" });
source.Add(new { Name = "liming" });
var dest = source.To<List<IPeople>>();
Assert.AreEqual(2, dest.Count);
Assert.AreEqual("huangxd", dest[0].Name);
}
To 方法是 ToType 方法的泛型版本,如果已知 Type 类型则使用 ToType 方法。
你可能使用过 AutoMapper
吧,To 方法也可以使用类似的映射器来进行对象转换。
[TestMethod]
public void TestMapTo()
{
var mapper = new ConvertMapper<Data1, Data2>()
.Map(s => s.Description, s => s.Name + " test")
.Map(s => s.Other, s => "other");
var data1 = new Data1 { Name = "fireasy" };
var data2 = data1.To(mapper);
Assert.AreEqual("fireasy test", data2.Description);
Assert.AreEqual("other", data2.Other);
}
7、范围判断
IsBetween 方法用于判断指定的值是否在最小值和最大值之间,它通过提供的一个 IComparer 实例来完成比较。
8、对象扩展
Extend方 法类似 jQuery
中的 Extend 方法,可以对源对象进行属性扩展,构造新的动态对象。
[TestMethod]
public void TestExtend()
{
object obj = new { Name = "fireasy", Sex = 0 };
dynamic obj1 = obj.Extend(new { Address = "kunming" });
Assert.AreEqual("fireasy", obj1.Name);
Assert.AreEqual("kunming", obj1.Address);
}
Extend 方法扩展的新对象是动态类型的,然而很多时候更希望扩展的新对象是某一特定类型,因此 Extend 方法还提供了一个泛型版本 ExtendAs,泛型参数即新对象的类型。
public class Data1
{
public string Name { get; set; }
}
public class Data2
{
public string Sex { get; set; }
public int Age { get; set; }
}
[TestMethod]
public void TestExtendAs()
{
var d1 = new Data1 { Name = "fireasy" };
var d2 = d1.ExtendAs<Data2>(new { Age = 12 });
var d3 = d2.ExtendAs<Data2>(new { Sex = "男" });
Assert.AreEqual(12, d3.Age);
Assert.AreEqual("男", d3.Sex);
}
9、转为动态对象
ToDynamic 将一个对象转换为动态对象。
[TestMethod]
public void TestToDynamic()
{
dynamic obj = new { Name = "fireasy" }.ToDynamic();
var dic = obj as IDictionary<string, object>;
Assert.AreEqual("fireasy", obj.Name);
Assert.AreEqual("fireasy", dic["Name"]);
}
💡 扩展阅读
Fireasy 里提供了一个 DynamicExpandoObject
类型,ToDynamic 转换后的对象属于此类型,因此可使用 IDictionary<string, object>
进行转换。