我正在尝试创建一个使用工厂查找正确类的类,然后调用该类的方法。 我做错了,因为Visual Studio中的intellisense一直警告我错误,当我尝试访问应该在工厂返回的类中的方法时,它们不可用。
谁能告诉我我做错了什么?
以下是我试图获取对该类的引用的代码:
DBBase dal = DALFactory.GetDAL(typeof(T));
myCollection = dal.GetByCriteria(myCriteria, sortExpression, startRowIndex, maximumRows, propertyNamesToBypassInstantiation);
以下是DALFactory类的代码:
internal static class DALFactory
{
// GetParser
internal static DBBase GetDAL(System.Type BOType)
{
switch (BOType.Name)
{
case "Person":
return new PersonDB();
}
// if we reach this point then we failed to find a matching type. Throw
// an exception.
throw new Exception("Unknown Type");
}
}
以下是工厂类返回的类的基础代码的一部分:
public abstract class DBBase
{
protected static T GetSingleBO<T>(ref SqlCommand command) where T : BOBase
{
return GetSingleBO<T>(ref command, null);
}
protected static T GetSingleBO<T>(ref SqlCommand command, List<string> propertyNamesToBypassInstantiation) where T : BOBase
{
T BO = default(T);
try
{
command.Connection.Open();
SqlDataReader reader = command.ExecuteReader();
if (reader.HasRows)
{
reader.Read();
BOParser parser = BOParserFactory.GetParser(typeof(T));
parser.PopulateOrdinals(reader);
if (propertyNamesToBypassInstantiation == null)
{
BO = (T)parser.PopulateBO(reader);
}
else
{
BO = (T)parser.PopulateBO(reader, propertyNamesToBypassInstantiation);
}
reader.Close();
}
else
{
// Whever there's no data, we return null.
BO = default(T);
}
}
catch (Exception ex)
{
StringBuilder sb = new StringBuilder();
sb.AppendFormat("Error populating data: {0}", ex.Message);
if (ex.InnerException != null)
{
sb.AppendFormat(" -- Inner Exception: {0}", ex.InnerException.Message);
}
throw new Exception(sb.ToString(), ex);
}
finally
{
command.Connection.Close();
command.Connection.Dispose();
}
// return the BO, it's either populated with data or null.
return BO;
}
protected static EquatableList<T> GetBOEquatableList<T>(ref SqlCommand command) where T : BOBase
{
return GetBOEquatableList<T>(ref command, null);
}
protected static EquatableList<T> GetBOEquatableList<T>(ref SqlCommand command, List<string> propertyNamesToBypassInstantiation) where T : BOBase
{
EquatableList<T> BOList = new EquatableList<T>();
try
{
command.Connection.Open();
SqlDataReader reader = command.ExecuteReader();
if (reader.HasRows)
{
// Get a parser for this BO type and populate
// the ordinals.
BOParser parser = BOParserFactory.GetParser(typeof(T));
parser.PopulateOrdinals(reader);
// Use the parser to build our list of BOs.
while (reader.Read())
{
T BO = default(T);
if (propertyNamesToBypassInstantiation == null)
{
BO = (T)parser.PopulateBO(reader);
}
else
{
BO = (T)parser.PopulateBO(reader, propertyNamesToBypassInstantiation);
}
BOList.Add(BO);
}
reader.Close();
}
}
catch (Exception ex)
{
StringBuilder sb = new StringBuilder();
sb.AppendFormat("Error populating data: {0}", ex.Message);
if (ex.InnerException != null)
{
sb.AppendFormat(" -- Inner Exception: {0}", ex.InnerException.Message);
}
throw new Exception(sb.ToString(), ex);
}
finally
{
command.Connection.Close();
command.Connection.Dispose();
}
return BOList;
}
protected static int GetBOCount(ref SqlCommand command)
{
int count = 0;
try
{
command.CommandType = CommandType.StoredProcedure;
DbParameter idParam = command.CreateParameter();
idParam.DbType = DbType.Int32;
idParam.Direction = ParameterDirection.InputOutput;
idParam.ParameterName = "@recordCount";
idParam.Value = 0;
command.Parameters.Add(idParam);
command.Connection.Open();
command.ExecuteNonQuery();
command.Connection.Close();
count = (int)command.Parameters["@recordCount"].Value;
}
catch (Exception e)
{
throw new Exception("Error populating data", e);
}
finally
{
command.Connection.Close();
command.Connection.Dispose();
}
return count;
}
protected static bool DeleteBO(ref SqlCommand command)
{
int result = 0;
try
{
command.Connection.Open();
result = command.ExecuteNonQuery();
}
catch (Exception e)
{
throw new Exception("Error deleting data", e);
}
finally
{
command.Connection.Close();
command.Connection.Dispose();
}
return result > 0;
}
protected static int GetFKcount(ref SqlCommand command)
{
int count = 0;
try
{
command.CommandType = CommandType.StoredProcedure;
DbParameter idParam = command.CreateParameter();
idParam.DbType = DbType.Int32;
idParam.Direction = ParameterDirection.InputOutput;
idParam.ParameterName = "@recordCount";
idParam.Value = 0;
command.Parameters.Add(idParam);
command.Connection.Open();
command.ExecuteNonQuery();
command.Connection.Close();
count = (int)command.Parameters["@recordCount"].Value;
}
catch (Exception e)
{
throw new Exception("Error populating data", e);
}
finally
{
command.Connection.Close();
command.Connection.Dispose();
}
return count;
}
public static T GetById<T>(Guid Id)
where T : BOBase
{
return GetById<T>(Id, null);
}
abstract public static T GetById<T>(Guid Id, List<string> propertyNamesToBypassInstantiation)
where T : BOBase;
public static EquatableList<T> GetByCriteria<T,TCriteria>(TCriteria myCriteria)
where T : BOBase
where TCriteria : BaseCriteria
{
return GetByCriteria<T, TCriteria>(myCriteria, null);
}
public static EquatableList<T> GetByCriteria<T, TCriteria>(TCriteria myCriteria, List<string> propertyNamesToBypassInstantiation)
where T : BOBase
where TCriteria : BaseCriteria
{
return GetByCriteria<T, TCriteria>(myCriteria, null, -1, -1, propertyNamesToBypassInstantiation);
}
public static EquatableList<T> GetByCriteria<T, TCriteria>(TCriteria myCriteria, string sortExpression, int startRowIndex, int maximumRows)
where T : BOBase
where TCriteria : BaseCriteria
{
return GetByCriteria<T, TCriteria>(myCriteria, sortExpression, startRowIndex, maximumRows, null);
}
abstract public static EquatableList<T> GetByCriteria<T, TCriteria>(TCriteria myCriteria, string sortExpression, int startRowIndex, int maximumRows, List<string> propertyNamesToBypassInstantiation)
where T : BOBase
where TCriteria : BaseCriteria;
abstract public static int GetCountForCriteria<TCriteria>(TCriteria myCriteria)
where TCriteria : BaseCriteria;
abstract public static bool Save<T>(T myobject)
where T : BOBase;
abstract public static bool Delete<T>(Guid Id)
where T : BOBase;
abstract public static int GetFKcount<T>(Guid Id)
where T : BOBase;
abstract internal static void CreateCriteriaParameters<T, TCriteria>(ref SqlCommand myCommand, TCriteria myCriteria)
where T : BOBase
where TCriteria : BaseCriteria;
}
这是应该返回的最终底层类的代码的一部分:
public partial class PersonDB : DBBase
{
public override static Person GetById(Guid Id, List<string> propertyNamesToBypassInstantiation)
{
SqlCommand command = GetDbSprocCommand("sprocPersonSelectSingleItem");
command.Parameters.Add(CreateParameter("@id", Id));
return GetSingleBO<Person>(ref command, propertyNamesToBypassInstantiation);
}
public override static EquatableList<Person> GetByCriteria(PersonCriteria myCriteria, string sortExpression, int startRowIndex, int maximumRows, List<string> propertyNamesToBypassInstantiation)
{
SqlCommand command = GetDbSprocCommand("sprocPersonSearchList");
CreateCriteriaParameters(ref command, myCriteria);
CreatePagingAndSortingParameters(ref command, sortExpression, startRowIndex, maximumRows);
return GetBOEquatableList<Person>(ref command, propertyNamesToBypassInstantiation);
}
}
答案 0 :(得分:1)
嗯,DBBase被定义为非泛型;这意味着你不能声明它的通用实例。这是我看到的最大的编译时错误。
通常,开启类型是一个Rally Bad Idea,因为您希望此工厂能够生产的每种新类型都需要一个新案例。在几十个新类型之后,这个switch语句将太长而无法有效维护。试试这个工厂:
public class DALFactory
{
private Dictionary<Type, Func<DBBase>> factoryMethods;
public void Register(Type type, Func<DBBase>> producer)
{
factoryMethods[type] = producer; //last-in wins
}
public DBBase GetObject(Type type)
{
if(factoryMethods.ContainsKey(type))
return factoryMethods[type]();
return null; //or throw exception
}
public Func<DBBase> GetFactoryMethod(Type type)
{
if(factoryMethods.ContainsKey(type))
return factoryMethods[type];
return null; //or throw exception
}
}
...
var factory = new DALFactory();
factory.Register(typeof(PersonDB), ()=>new PersonDB());
//Register other named or anonymous factory methods that produce objects
在设置工厂时,您可以根据需要注册任意数量的工厂方法,而无需更改工厂本身。
答案 1 :(得分:1)
虽然关于如何制作一个漂亮的通用工厂的所有评论都很好,但这不是我的问题。我遇到的问题是,即使我的工厂正在返回对类的引用,我也无法访问该类中的任何方法。
事实证明,如果你在一个类上有静态方法,那么当你拥有该类的“实例”时它们就不可用了。由于派生类中的所有方法都是静态的,我以为我实际上并没有回到我的类的实例。事实证明我是....
无论如何,因为我真正需要的是一种获取派生类中方法的引用的方法,我找到了一个使用GetType,GetMethod和MethoInfo.Invoke来调用方法的解决方案。
以下是一个例子:
我最初尝试获取对派生类的引用的方法:
public static bobase GetByCriteria<T,TCriteria>(TCriteria myCriteria)
where T : bobase
where TCriteria : BaseCriteria
{
T myObject = default(T);
Type[] myMethodTypes = new Type[]{typeof(string),typeof(int),typeof(TCriteria)};
System.Reflection.MethodInfo myMethod = myObject.DBclass.GetMethod("GetByCriteria", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static, null, myMethodTypes, null);
object[] myMethodParameters = new object[]{"someValueHere", 1, myObject};
return (bobase)myMethod.Invoke(null, myMethodParameters);
}
bobase类:
public abstract class bobase
{
internal virtual Type DBclass
{
get
{
return Type.GetType(this.GetType().Name + "DB");
}
}
}
我知道GetMethod的东西使用反射来获取数据,因此可能存在某种性能损失。如果有更好的方法来获取不使用反射的静态方法(如静态方法工厂)的引用,我很乐意听到它。
答案 2 :(得分:0)
我在我的项目中使用这样的工厂:
public class SomeFactory
{
private static SomeFactory instance = new SomeFactory();
public static SomeFactory Instance
{
get
{
return instance;
}
}
private static Dictionary<Type, Type> classes = new Dictionary<Type, Type>();
static SomeFactory()
{
// add here classes that would be created by a factory
classes.Add(typeof(IClass1), typeof(Class1));
classes.Add(typeof(IClass2), typeof(Class2));
}
public T Create<T>()
{
return (T)System.Activator.CreateInstance(repos[typeof(T)]);
}
}
使用
SomeFactory.Instance.Create<IClass1>()...
答案 3 :(得分:0)
为什么不使用属性并创建动态工厂
公共静态类MyFactory { private static Dictionary objects = new Dictionary();
static MyFactory()
{
// Scan the assembly
foreach (Type type in Assembly.GetExecutingAssembly().GetTypes())
{
MyAttribute[] frameAttributes =
type.GetCustomAttributes(typeof (MyAttribute), false) as MyAttribute[];
foreach (MyAttribute myAttribute in frameAttributes)
{
objects.Add(myAttribute.SomeKey, type);
}
}
}