如果想创建一个包含在一个未加载程序集中的类,一般可以使用两种方法:
(1)使用System.Activator.CreateInstance()方法;
(2)使用反射获取类的构造函数的签名信息,并使用Invoke方法调用。
以下简单的举了两个例子说明这两种方法的用法:
Type type = Type.GetType("NothinButAspNet.SqlMembershipProvider,SqlMembershipProvider");
System.Reflection.ConstructorInfo csInfo = type.GetConstructor(new Type[]{typeof(string)});
NothinButAspNet.SqlMembershipProvider provider = (NothinButAspNet.SqlMembershipProvider) csInfo.Invoke(new object[]{"connectionstring"});
provider = (NothinButAspNet.SqlMembershipProvider) Activator.CreateInstance(type, new object[]{"connectonStringActivator"});
System.Reflection.ConstructorInfo csInfo = type.GetConstructor(new Type[]{typeof(string)});
NothinButAspNet.SqlMembershipProvider provider = (NothinButAspNet.SqlMembershipProvider) csInfo.Invoke(new object[]{"connectionstring"});
provider = (NothinButAspNet.SqlMembershipProvider) Activator.CreateInstance(type, new object[]{"connectonStringActivator"});
其中:NothinButAspNet.SqlMembershipProvider表示要创建的类的名字(包括命名控件),SqlMembershipProvider是该类所在的程序集。
那么两种方法在效果和性能有没有具体的区别呢?
1、应该说两种方法在效果上是没有区别的。
2、在性能上,两者应该也没有本质的区别,这一点只要我们通过反编译System.Activator这个类就可以看到:
object CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, object[] args, CultureInfo culture, object[] activationAttributes)
{
object obj1 = null;
try
{
try
{
bool flag1;
object obj2;
if (activationAttributes != null)
{
ActivationServices.PushActivationAttributes(this, activationAttributes);
}
if (args == null)
{
args = new object[0];
}
int num1 = args.Length;
if (binder == null)
{
binder = Type.DefaultBinder;
}
if ((((num1 == 0) && ((bindingAttr & BindingFlags.Public) != BindingFlags.Default)) && ((bindingAttr & BindingFlags.Instance) != BindingFlags.Default)) && (this.IsGenericCOMObjectImpl() || this.IsSubclassOf(RuntimeType.valueType)))
{
return this.CreateInstanceImpl(((bindingAttr & BindingFlags.NonPublic) == BindingFlags.Default) && true);
}
MethodBase[] baseArray1 = this.GetMemberCons(bindingAttr, CallingConventions.Any, null, num1, false, out flag1);
if (baseArray1 == null)
{
if (activationAttributes != null)
{
ActivationServices.PopActivationAttributes(this);
activationAttributes = null;
}
throw new MissingMethodException(string.Format(Environment.GetResourceString("MissingConstructor_Name"), this.FullName));
}
if (((num1 == 0) && (baseArray1.Length == 1)) && ((bindingAttr & BindingFlags.OptionalParamBinding) == BindingFlags.Default))
{
return Activator.CreateInstance(this, true);
}
MethodBase base1 = binder.BindToMethod(bindingAttr, baseArray1, ref args, null, culture, null, out obj2);
if (base1 == null)
{
if (activationAttributes != null)
{
ActivationServices.PopActivationAttributes(this);
activationAttributes = null;
}
throw new MissingMethodException(string.Format(Environment.GetResourceString("MissingConstructor_Name"), this.FullName));
}
if (flag1)
{
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
}
obj1 = ((ConstructorInfo) base1).Invoke(bindingAttr, binder, args, culture);
if (obj2 != null)
{
binder.ReorderArgumentArray(ref args, obj2);
}
return obj1;
}
finally
{
if (activationAttributes != null)
{
ActivationServices.PopActivationAttributes(this);
}
}
}
catch (Exception)
{
throw;
}
return obj1;
}
{
object obj1 = null;
try
{
try
{
bool flag1;
object obj2;
if (activationAttributes != null)
{
ActivationServices.PushActivationAttributes(this, activationAttributes);
}
if (args == null)
{
args = new object[0];
}
int num1 = args.Length;
if (binder == null)
{
binder = Type.DefaultBinder;
}
if ((((num1 == 0) && ((bindingAttr & BindingFlags.Public) != BindingFlags.Default)) && ((bindingAttr & BindingFlags.Instance) != BindingFlags.Default)) && (this.IsGenericCOMObjectImpl() || this.IsSubclassOf(RuntimeType.valueType)))
{
return this.CreateInstanceImpl(((bindingAttr & BindingFlags.NonPublic) == BindingFlags.Default) && true);
}
MethodBase[] baseArray1 = this.GetMemberCons(bindingAttr, CallingConventions.Any, null, num1, false, out flag1);
if (baseArray1 == null)
{
if (activationAttributes != null)
{
ActivationServices.PopActivationAttributes(this);
activationAttributes = null;
}
throw new MissingMethodException(string.Format(Environment.GetResourceString("MissingConstructor_Name"), this.FullName));
}
if (((num1 == 0) && (baseArray1.Length == 1)) && ((bindingAttr & BindingFlags.OptionalParamBinding) == BindingFlags.Default))
{
return Activator.CreateInstance(this, true);
}
MethodBase base1 = binder.BindToMethod(bindingAttr, baseArray1, ref args, null, culture, null, out obj2);
if (base1 == null)
{
if (activationAttributes != null)
{
ActivationServices.PopActivationAttributes(this);
activationAttributes = null;
}
throw new MissingMethodException(string.Format(Environment.GetResourceString("MissingConstructor_Name"), this.FullName));
}
if (flag1)
{
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
}
obj1 = ((ConstructorInfo) base1).Invoke(bindingAttr, binder, args, culture);
if (obj2 != null)
{
binder.ReorderArgumentArray(ref args, obj2);
}
return obj1;
}
finally
{
if (activationAttributes != null)
{
ActivationServices.PopActivationAttributes(this);
}
}
}
catch (Exception)
{
throw;
}
return obj1;
}
实际上Activator也是通过反射来实现的,所以两者应该没有明显的性能区别。
但是在查看微软提高的Provider Pattern的例子中却发现,他们使用的是第一种方法,为什么呢?
实际上通过下面的代码可以看到,虽然同样通过反射,但是使用我们却可以先把通过反射获取得到的构造函数信息缓存起来,下次直接调用Invoke方法,这样性能应该是要比直接使用Activator.CreateInstance要高的。
public static MembershipProvider Instance() {
// Use the cache because the reflection used later is expensive
Cache cache = HttpRuntime.Cache;
Type type = null;
string cacheKey = null;
// Get the names of the providers
MembershipConfiguration config = MembershipConfiguration.GetConfig();
// Read the configuration specific information
// for this provider
Provider membershipProvider = (Provider) config.Providers[config.DefaultProvider];
// In the cache?
cacheKey = "Membership::" + config.DefaultProvider;
if ( cache[cacheKey] == null ) {
// The assembly should be in \bin or GAC, so we simply need
// to get an instance of the type
try {
type = Type.GetType( membershipProvider.Type );
// Insert the type into the cache
Type[] paramTypes = new Type[1];
paramTypes[0] = typeof(string);
cache.Insert( cacheKey, type.GetConstructor(paramTypes) );
} catch (Exception e) {
throw new Exception("Unable to load provider", e);
}
}
// Load the configuration settings
object[] paramArray = new object[1];
paramArray[0] = membershipProvider.Attributes["connectionString"];
return (MembershipProvider)( ((ConstructorInfo)cache[cacheKey]).Invoke(paramArray) );
}
// Use the cache because the reflection used later is expensive
Cache cache = HttpRuntime.Cache;
Type type = null;
string cacheKey = null;
// Get the names of the providers
MembershipConfiguration config = MembershipConfiguration.GetConfig();
// Read the configuration specific information
// for this provider
Provider membershipProvider = (Provider) config.Providers[config.DefaultProvider];
// In the cache?
cacheKey = "Membership::" + config.DefaultProvider;
if ( cache[cacheKey] == null ) {
// The assembly should be in \bin or GAC, so we simply need
// to get an instance of the type
try {
type = Type.GetType( membershipProvider.Type );
// Insert the type into the cache
Type[] paramTypes = new Type[1];
paramTypes[0] = typeof(string);
cache.Insert( cacheKey, type.GetConstructor(paramTypes) );
} catch (Exception e) {
throw new Exception("Unable to load provider", e);
}
}
// Load the configuration settings
object[] paramArray = new object[1];
paramArray[0] = membershipProvider.Attributes["connectionString"];
return (MembershipProvider)( ((ConstructorInfo)cache[cacheKey]).Invoke(paramArray) );
}