代码之家  ›  专栏  ›  技术社区  ›  Amanda Kitson

类的泛型工厂?

  •  0
  • Amanda Kitson  · 技术社区  · 16 年前

    我正在尝试创建一个类,该类使用工厂查找正确的类,然后调用该类上的方法。 我做错了,因为visualstudio中的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);
        }
    }
    
    4 回复  |  直到 16 年前
        1
  •  1
  •   KeithS    16 年前

    DBBase被定义为非泛型;这意味着您不能声明它的泛型实例。这是我看到的最大的编译时错误。

    一般来说,换上这种型号是个坏主意,因为你想让这家工厂生产的每一种新型号都需要一个新的箱子。经过几十个新类型之后,这个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
    

    在设置工厂时,可以注册任意多个工厂方法,而无需更改工厂本身。

        2
  •  1
  •   Amanda Kitson    16 年前

    尽管所有关于如何制造一个好的通用工厂的评论都很好,但这不是我的问题。我遇到的问题是,即使我的工厂返回对该类的引用,我也无法访问该类上的任何方法。

    事实证明,如果你在一个类上有静态方法,当你有一个类的“实例”时,它们就不可用了。因为我在派生类中的所有方法都是静态的,所以我认为我实际上根本没有得到类的实例。原来我是。。。。

    无论如何,因为我真正需要的是一种方法来获取对派生类中方法的引用,所以我找到了一个使用GetType、GetMethod和方法信息调用改为调用方法。

    我最初尝试获取对派生类的引用的方法:

    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);
            }
    

    基本类:

    public abstract class bobase
        {
            internal virtual Type DBclass
            {
                get
                {
                    return Type.GetType(this.GetType().Name + "DB");
                }
            }
        }
    

    我知道GetMethod使用反射来获取数据,因此可能会对性能造成某种影响。如果有更好的方法来获取对不使用反射的静态方法(比如静态方法工厂)的引用,我很想听听。

        3
  •  0
  •   Sebastian Brózda    16 年前

    我在我的项目中使用这样一个工厂:

    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>()...
    
        4
  •  0
  •   Mark    16 年前

    为什么不使用属性并创建一个动态工厂呢

    公共静态类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);
                }
            }
        }