Лень, рефлексия, атрибуты, динамические сборки

от автора

Небольшой опыт, полученный благодаря лени

Года три назад, работал я на одну фирму. Было нас 4 программиста. Один писал бизнес логику приложения. Описывал он ее с помощью интерфейсов (interface). Логические связи, зависимости и т. д. Наша же задача была реализовать эти интерфейсы и связать с GUI. Основной проблемой в этой системе были постоянные изменения структуры связей и параметров. То есть нам приходилось постоянно заниматься правкой и рефакторингом.

Я человек достаточно ленивый. Поэтому пришла мысль — неужели нельзя, как нибудь это автоматизировать. И я сел за книжки.

Шаг первый

Первая идея была достаточна явна и проста. Интерфейсы содержаться в отдельных файлах — так почему бы не распарсить их и создать текстовой файл со сгенерированным классом. Так и было сделано.

К сожалению тех исходников не сохранилось, но есть аналог, кому интересно может посмотреть(классы строятся на основе таблиц из базы данных)

using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Data; using SV.DataBaseWork; using v2; using System.Data.SqlClient; namespace CreatorDBClasses {     class ColumnDB     {         public ColumnDB(string name, string type)         {             Name = name;             Type = ConvertType(type);         }         public string Name;         public string Type;         public string Initial = "null";         public string ConvertType(string tp)         {             switch (tp)             {                 case "String":                     Initial = "\"\"";                     return "string";                 case "Double":                     Initial = "0";                     return "double";                 case "Boolean":                     Initial = "false";                     return "bool";                 case "Int32":                     Initial = "0";                     return "int";                 default:                     Initial = "null";                     return tp;             }         }     }     /// <summary>     /// Сводное описание для CreatorDBWorkClasses     /// </summary>     public class CreatorClasses     {         String connStr = null;         public CreatorClasses(string table, String ConnectionString = null)         {             tablename = table;             className = "cl_" + tablename;             if (string.IsNullOrEmpty(ConnectionString))                 connStr = v2_SacuraConstants.ConnectionString;             else                 connStr = ConnectionString;             //             // TODO: добавьте логику конструктора             //         }         string mBaseClass = "DataBaseWorkBase";         public string BaseClass         {             get { return mBaseClass; }             set { mBaseClass = value; }         }          string tab = "\t\t";         string className;         string sources;         public string Sources         {             get { return sources; }         }         string tablename;         List<ColumnDB> mListColumn = new List<ColumnDB>();         public bool GetSources()         {             sources = "\tpublic class " + className + (string.IsNullOrEmpty(BaseClass) ? "" : " : " + BaseClass) + ", IDisposable\r\n\t{\r\n";             sources += GetConstructor();             sources += GetProperty();             sources += GetInitFunction();             sources += GetSaveFunction();             sources += GetDeleteFunction();             sources += GetDisposable();             sources += StaticGetList();             sources += GetLastError();             sources += "\t}";             return true;         }         string GetLastError()         {             return "#region Error\r\n" +                 "        string mError;\r\n" +                 "        public string lastError\r\n" +                 "        {\r\n" +                 "            get { return mError; }\r\n" +                 "        }\r\n" +                 "#endregion\r\n";         }         string StaticGetList()         {             return "#region Static_GetList\r\n" +                    "     public static List<"+className+"> GetList(string SQL_WHERE = \"\")\r\n" +                    "     {\r\n" +                    "         List<" + className + "> lst = null;\r\n" +                    "         DataBaseWorkBase db = new DataBaseWorkBase(v2_SacuraConstants.ConnectionString);\r\n" +                    "         if (db.Open())\r\n" +                    "         {\r\n" +                    "             lst = new List<" + className + ">();\r\n" +                    "             SqlCommand sqlcmd = db.CreateSQLCommand();\r\n" +                    "             sqlcmd.CommandText = \"SELECT * \" +\r\n" +                    "                 \"FROM "+tablename+" \" + SQL_WHERE;\r\n" +                    "             SqlDataReader reader = sqlcmd.ExecuteReader();\r\n" +                    "             while (reader.Read())\r\n" +                    "             {\r\n" +                    "                 " + className + " ord = new " + className + "();\r\n" +                    "                 if (ord.InitFromDataReader(reader))\r\n" +                    "                     lst.Add(ord);\r\n" +                    "             }\r\n" +                    "             reader.Close();\r\n" +                    "             reader = null;\r\n" +                    "         }\r\n" +                    "         db.Close();\r\n" +                    "         db = null;\r\n" +                    "         return lst;\r\n" +                    "     }\r\n" +                 "#endregion\r\n";         }         string GetDisposable()         {             return "#region Члены IDisposable\r\n" +                    "     public override void Close()\r\n" +                    "     {\r\n" +                    "         base.Close();\r\n" +                    "     }\r\n" +                    "\r\n" +                    "    protected override void Dispose(bool disposing)\r\n" +                    "    {\r\n" +                    "        if (disposing)\r\n" +                    "        {\r\n"+                    "            Close();\r\n" +                    "            base.Dispose(true);\r\n" +                    "        }\r\n" +                    "    }\r\n" +                                      "#endregion\r\n";         }         string GetDeleteFunction()         {             string con = "#region Delete\r\n"+                             tab+"public bool Delete()\r\n"+                  tab+"{\r\n"+                  tab+"    bool result = false;\r\n"+                  tab+"    try\r\n"+                  tab+"    {\r\n"+                  tab+"        SqlCommand sqlcmd = CreateSQLCommand();\r\n"+                  tab+"        sqlcmd.CommandText = \"DELETE FROM "+tablename+" WHERE ID=@ID\";\r\n"+                  tab+"        sqlcmd.Parameters.AddWithValue(\"@ID\", m_ID);\r\n"+                  tab+"        sqlcmd.ExecuteNonQuery();\r\n"+                  tab+"        result = true;\r\n"+                  tab+"    }\r\n"+                  tab+"    catch (System.Exception ex)\r\n"+                  tab+"    {\r\n"+                  tab+"    }\r\n"+                  tab+"    return result;\r\n"+                  tab+"}\r\n"+                  "#endregion\r\n";             return con;         }         string GetInitParams()         {             string pr = "";             int cnt = mListColumn.Count;             for (int a = 0; a < cnt; a++)             {                 if (mListColumn[a].Type != "string")                     pr += tab + tab + mListColumn[a].Type + ".TryParse(reader[" + a.ToString() + "].ToString(), out m_" + mListColumn[a].Name + ");\r\n";                 else                     pr += tab + tab + mListColumn[a].Name +"=reader[" + a.ToString() + "].ToString();\r\n";             }                       return pr;         }         string GetInitFunction()         {             string fn = "#region Init\r\n" +                     tab + "public bool InitFromDataReader(SqlDataReader reader)\r\n" +                     tab + "{\r\n" +                     tab + "    try\r\n" +                     tab + "    {\r\n" +                     GetInitParams() +                     tab + "    }\r\n" +                     tab + "    catch (System.Exception ex)\r\n" +                     tab + "    {\r\n" +                     tab + "        return false;\r\n" +                     tab + "    }\r\n" +                     tab + "    return true;\r\n" +                     tab + "}\r\n" +                     tab + "public bool Init(string id)\r\n" +                     tab + "{\r\n" +                     tab + "    if (string.IsNullOrEmpty(id))\r\n" +                     tab + "        return false;\r\n" +                     tab + "    int t;\r\n" +                     tab + "    int.TryParse(id, out t);\r\n" +                     tab + "    return Init(t);\r\n" +                     tab + "}\r\n" +                     tab + "public bool Init(int id)\r\n" +                     tab + "{\r\n" +                     tab + "   if (!base.Open())\r\n" +                     tab + "       return false;\r\n" +                     tab + "   bool IsLoad = false;\r\n" +                     tab + "   try\r\n" +                     tab + "   {\r\n" +                     tab + "       SqlCommand sqlcmd = CreateSQLCommand();\r\n" +                     tab + "       sqlcmd.CommandText = \"SELECT * \" +\r\n" +                     tab + "           \"FROM " + tablename + " WHERE [ID]=@ID\";\r\n" +                     tab + "       sqlcmd.Parameters.AddWithValue(\"@ID\", id);\r\n" +                     tab + "       SqlDataReader reader = sqlcmd.ExecuteReader();\r\n" +                     tab + "       if (reader.Read())\r\n" +                     tab + "       {\r\n" +                     tab + "          if (!InitFromDataReader(reader))\r\n" +                     tab + "            {\r\n" +                     tab + "              reader.Close();\r\n" +                     tab + "              base.Close();\r\n"+                     tab + "              return false;\r\n" +                     tab + "            }\r\n" +                     tab + "           IsLoad = true;\r\n" +                     tab + "       }\r\n" +                     tab + "       reader.Close();\r\n" +                     tab + "   }\r\n" +                     tab + "   catch (System.Exception ex)\r\n" +                     tab + "   {\r\n" +                     tab + "       mError = ex.Message;\r\n" +                     tab + "       IsLoad = false;\r\n" +                     tab + "   }\r\n" +                     tab + "   base.Close();" +                     tab + "   return IsLoad;\r\n" +                     tab + "}\r\n";             fn += "#endregion\r\n";             return fn;         }         string GetConstructor()         {             string con = "#region Constructor\r\n" +                 tab + "public " + className + "(string ConnectionString)\r\n" +                 tab + "\t: base (ConnectionString)\r\n" +                 tab + "{\r\n" +                 tab + "}\r\n" +                 tab + "public " + className + "()\r\n" +                 tab + "\t:base(v2_SacuraConstants.ConnectionString)\r\n" +                 tab + "{\r\n" +                 tab + "}\r\n" +                 "#endregion\r\n";             return con;         }         string GetProperty()         {             mListColumn.Clear();             string src = "#region Property\r\n";             //////////////////////////////////////////////////////////////////////////             //add property             SqlConnection myConnection = new SqlConnection(connStr);             SqlDataAdapter myAdapter = new SqlDataAdapter("select * from " + tablename, myConnection);             DataSet dataSet = new DataSet();             myConnection.Open();             myAdapter.Fill(dataSet, "tablename");             myConnection.Close();             ConstraintCollection prKey = dataSet.Tables[0].Constraints;              for (int i = 0; i < dataSet.Tables[0].Columns.Count; i++)             {                 string tab1 = "\t\t\t";                 src += tab;                 ColumnDB clTp = new ColumnDB(dataSet.Tables[0].Columns[i].ColumnName, dataSet.Tables[0].Columns[i].DataType.Name);                 mListColumn.Add(clTp);                 src += clTp.Type + " m_" + clTp.Name +"="+clTp.Initial+ ";\r\n";                 src += "\t\t";                 src += "public "+clTp.Type + " " + clTp.Name + "\r\n" + tab + "{\r\n" +                     tab1 + "get\r\n" + tab1 + "{\r\n" + tab1 + "\treturn m_" + clTp.Name + ";\r\n" + tab1 + "}\r\n" + tab1 +                     "set\r\n" + tab1 + "{\r\n" + tab1 + "\tm_" + clTp.Name + "=value;\r\n" + tab1 + "}\r\n" + tab + "}\r\n";             }             //////////////////////////////////////////////////////////////////////////             return src + "#endregion\r\n";         }         string GetSaveInsertParams()         {             string pr = "";             int cnt = mListColumn.Count;             for (int a = 1; a < cnt; a++)             {                 pr += tab + tab + tab + (a == 1 ? "\"[" : "\",[") + mListColumn[a].Name + "]\"+\r\n";             }             return pr;         }         string GetSaveInsertValues()         {             string pr = "";             int cnt = mListColumn.Count;             for (int a = 1; a < cnt; a++)             {                 pr += tab + tab + tab + (a == 1 ? "\"@" : "\",@") + mListColumn[a].Name + (a != cnt - 1 ? "\"+\r\n" : ")\"");             }             return pr;         }         string GetSaveUpdateParams()         {             string pr = "";             int cnt = mListColumn.Count;             for (int a = 1; a < cnt; a++)             {                 pr += tab + tab + tab + (a == 1 ? "\"[" : "\",[") + mListColumn[a].Name + "]=@" + mListColumn[a].Name +                     "\"" + (a != cnt - 1 ? "+\r\n" : "");             }             return pr;         }         string GetAddWithValue()         {             string pr = "";             int cnt = mListColumn.Count;             for (int a = 1; a < cnt; a++)             {                 pr += tab + tab + "sqlcmd.Parameters.AddWithValue(\"@" + mListColumn[a].Name +"\", m_"+                     mListColumn[a].Name+");\r\n";             }             return pr;         }         string GetSaveFunction()         {             string con = "#region Save\r\n" +             tab + "public bool Save()\r\n" +             tab + "    {\r\n" +             tab + "        bool result = false;\r\n" +             tab + "        try\r\n" +             tab + "        {\r\n" +             tab + "            SqlCommand sqlcmd = CreateSQLCommand();\r\n" +             tab + "            if (m_ID <= 0)\r\n" +             tab + "            {\r\n" +             tab + "                sqlcmd.CommandText = \"INSERT INTO " + tablename + " (  \"+\r\n" +                     GetSaveInsertParams() +             tab + "                     \") VALUES (\"+\r\n" +                     GetSaveInsertValues() + "+\";SELECT CAST(scope_identity() AS int)\";\r\n" +             tab + "            }\r\n" +             tab + "            else\r\n" +             tab + "            {\r\n" +             tab + "                sqlcmd.CommandText = \"UPDATE " + tablename + " SET \" +\r\n" +                     GetSaveUpdateParams() + "+\r\n" +             tab + "                \" WHERE ID=@ID\";\r\n" +             tab + "                sqlcmd.Parameters.AddWithValue(\"@ID\", m_ID);\r\n" +             tab + "            }\r\n" +             tab + GetAddWithValue() + "\r\n" +             tab + "            if (m_ID > 0)\r\n" +             tab + "                sqlcmd.ExecuteNonQuery();\r\n" +             tab + "            else\r\n" +             tab + "            {\r\n" +             tab + "                object ob;\r\n" +             tab + "                ob = sqlcmd.ExecuteScalar();\r\n" +             tab + "                if(ob != null)\r\n" +             tab + "                    int.TryParse(ob.ToString(), out m_ID);\r\n" +             tab + "            }\r\n" +             tab + "        }\r\n" +             tab + "        catch (System.Exception ex)\r\n" +             tab + "        {\r\n" +             tab + "            mError = ex.Message;\r\n" +             tab + "            result = false;\r\n" +             tab + "        }\r\n" +             tab + "        return result;\r\n" +             tab + "    }\r\n" +             "#endregion\r\n";             return con;         }     }  } 

Вроде бы, проблема решена. Но тем не менее работы еще много оставалось: перенос файлов, рефакторинг. Да еще у ребят ничего не изменилось. Они занимались созданием UI и привязкой его к объектной модели.

Шаг второй

Продолжая поиски в сети я наткнулся на описание класс Type, заинтересовавшись, я почитал про него подробнее. Есть много интересных функций у этого класса. Фактически благодаря ему можно полностью получить всю информацию по классу. Конструктор, реализованные интерфейсы, свойства, переменные, функции… Полную информацию. И я начал эксперементировать с ним, и в итоге, получил:

Класс для работы с типами классов

using System; using System.Collections.Generic; using System.Reflection; using System.Threading;  namespace SV.Tools { 	public delegate void AddProp(string name, string val); 	/// <summary> 	/// структура для дублирующих параметров 	/// </summary> 	public struct MetodInfoDouble 	{ 		public MethodInfo getMetod; 		public MethodInfo setMetod; 	} 	/// <summary> 	/// класс со статик функциями для работы с классами :( ну и описалово получилось 	/// </summary> 	public class ClassesTools 	{ 		/// <summary> 		/// получения данных всех порперти класса включая вложения 		/// </summary> 		/// <param name="ob"></param> 		/// <param name="delegateProp"></param> 		/// <param name="parentName"></param> 		public static void GetAllPropertyData(object ob, AddProp delegateProp, string parentName = "") 		{ 			if (ob == null || delegateProp == null) 				return; 			PropertyInfo[] propInfo = ob.GetType().GetProperties(); 			for (int b = 0; b < propInfo.Length; b++) 			{ 				ParameterInfo[] param = propInfo[b].GetIndexParameters(); 				if (param.Length == 0 && propInfo[b].CanRead && propInfo[b].Name != "Root" && propInfo[b].Name != "Parent") 				{ 					object data = propInfo[b].GetValue(ob, null); 					if (propInfo[b].PropertyType.IsInterface && data != null) 					{ 							GetAllPropertyData(data, delegateProp, (parentName == "" ? "" : parentName + ".") + propInfo[b].Name);  					} 					else 					{ 						delegateProp((parentName == "" ? "" : parentName + ".") + propInfo[b].Name, 							( data == null ? "NULL" : SV.ConversionTools.DataCoversion.ConvertByType(data))); 					} 				} 			} 		}  		static AppDomain domain = Thread.GetDomain(); 		static Assembly[] asm = domain.GetAssemblies(); 		/// <summary> 		/// поиск интерфеса по имени 		/// </summary> 		/// <param name="name">имя интерфейса</param> 		/// <returns></returns> 		public static Type FindInterfece(string Namespace, string Name, bool isIgnorCase = true) 		{ 			if (Namespace == "" || Name == "") 				return null; 			 			int count = asm.Length; 			for (int a = 0; a < count; a++) 			{ 				Type t = asm[a].GetType(Namespace+"."+Name); 				if (t != null) 					return t; 			} 			return null; 		} 		public static List<Type> GetAsseblyIntrface(string AssembleName, string Namespace) 		{ 			if (Namespace == "" ) 				return null; 			int count = asm.Length; 			for (int a = 0; a < count; a++) 			{ 				if (asm[a].GetName().Name == AssembleName) 				{ 					Type[] t = asm[a].GetTypes(); 					List<Type> lst = new List<Type>(); 					count = t.Length; 					for(int b =0; b < count; b++) 					{ 						if (t[b].Namespace == Namespace) 							lst.Add(t[b]); 					} 					return lst; 				}				 			} 			return null; 		} 		/// <summary> 		/// находит все нтерфейсы, включая вложенные, удаляет дубликаты 		/// </summary> 		/// <param name="tp"></param> 		/// <param name="listInterfece"></param> 		public static void GetAllInterfece(Type[] tp, ref List<Type> listInterfece) 		{ 			if (tp == null) 				return; 			int count = tp.Length; 			for (int a = 0; a < count; a++) 			{ 				Type rezult = listInterfece.Find( 					delegate(Type typ) 					{ 						return tp[a] == typ; 					} 					); 				if (rezult == null) 					listInterfece.Add(tp[a]); 				Type[] t = tp[a].GetInterfaces(); 				GetAllInterfece(t, ref listInterfece); 			} 		} 		/// <summary> 		/// находит все нтерфейсы, включая вложенные 		/// </summary> 		/// <param name="parentClass"></param> 		/// <returns></returns> 		public static List<Type> GetAllInterfece(Type parentClass) 		{ 			List<Type> listClasses = new List<Type>(); 			GetAllInterfece(new Type[] { parentClass }, ref listClasses); 			return listClasses; 		} 		/// <summary> 		/// находит все нтерфейсы, включая вложенные, удаляет дубликаты 		/// </summary> 		/// <param name="parentClass"></param> 		/// <param name="listInterfece"></param> 		/// <returns></returns> 		public static List<Type> GetAllInterfece(Type parentClass, List<Type> listInterfece) 		{ 			List<Type> listClasses = new List<Type>(); 			GetAllInterfece(new Type[] { parentClass }, ref listClasses); 			GetAllInterfece(listInterfece.ToArray(), ref listInterfece); 			return RemoveDouble(listClasses, listInterfece); 		} 		/// <summary> 		/// удаляет дубликаты в списках 		/// </summary> 		/// <param name="sources">источник</param> 		/// <param name="editableList">список в котором уберуться встречающиеся значения в sources</param> 		/// <returns>возращаемое значение</returns> 		public static List<Type> RemoveDouble(List<Type> sources, List<Type> editableList) 		{ 			for (int a = editableList.Count - 1; a >= 0; a--) 			{ 				if (sources.Find((Type t) => editableList[a] == t) != null) 					editableList.RemoveAt(a); 			} 			return editableList; 		} 		/// <summary> 		/// поиск параметра во всех интерфейсах типа 		/// </summary> 		/// <param name="_class">тип класса</param> 		/// <param name="propertyName">имя параметра</param> 		/// <returns>найденный параметр</returns> 		/// <remarks> 		/// тестовая функция 		/// </remarks> 		public static PropertyInfo FindProperty(Type _class, string propertyName) 		{ 			List<PropertyInfo> allProperty = GetAllProperty(_class); 			int count = allProperty.Count; 			PropertyInfo info = null; 			for (int a = 0; a < count; a++) 			{ 				if (allProperty[a].Name == propertyName) 				{ 					info = allProperty[a]; 					break; 				} 			}  			return info; 		} 		public static List<Type> RemoveDouble(List<Type> property) 		{ 			List<Type> retryList = new List<Type>(); 			int count = property.Count; 			for (int a = 0; a < count; a++) 			{ 				if (retryList.Find((Type inf) => property[a] == inf) == null) 					retryList.Add(property[a]);  			} 			return retryList; 		}  		public static List<PropertyInfo> RemoveDouble(List<PropertyInfo> property) 		{ 			List<PropertyInfo> retryList = new List<PropertyInfo>(); 			int count = property.Count; 			for (int a = 0; a < count; a++) 			{ 				if(retryList.Find( (PropertyInfo inf) =>property[a] == inf	) == null) 					retryList.Add(property[a]);  			} 			return retryList; 		} 		/// <summary> 		/// получает все параметры по типу, с удалением дублей 		/// </summary> 		/// <param name="interfeceList">родидельский тип</param> 		/// <returns>список параметров</returns> 		/// <remarks> 		/// тестовая функция 		/// </remarks> 		public static List<PropertyInfo> GetAllProperty(Type parent) 		{ 			List<Type> allTypes = GetAllInterfece(parent); 			List<PropertyInfo> allProperty = new List<PropertyInfo>(); 			if (allTypes != null) 			{ 				int count = allTypes.Count; 				for(int a =0; a < count; a++) 				{ 					allProperty.AddRange(allTypes[a].GetProperties(/*BindingFlags.Default*/)); 				} 			} 			return RemoveDouble(allProperty); 		} 		/// <summary> 		/// поиск параметра по имени 		/// </summary> 		/// <param name="name">имя параметра</param> 		/// <returns></returns> 		public static PropertyInfo GetPropertyByName(object curr, string name, ref object objectIsCall) 		{ 			if (curr == null) 				return null; 			PropertyInfo pI = curr.GetType().GetProperty(name); 			objectIsCall = curr; 			if (pI == null) 			{  				int t = name.IndexOf('.'); 				if (t > 0) 				{ 					string curName = name.Substring(0, t); 					pI = curr.GetType().GetProperty(curName); 					if (pI != null) 					{ 						name = name.Remove(0, t + 1); 						if (name.Length > 0) 						{ 							object v = pI.GetValue(curr, null); 							if (v == null) 								return null; 							return GetPropertyByName(v, name, ref objectIsCall); 						} 					}  				} 			} 			return pI; 		} 		/// <summary> 		/// формирует списки метаданных на основании переданных типов 		/// </summary> 		/// <param name="interfeceList">список типов интерфейса\класса</param> 		/// <param name="propertyInfo">список для заполнения</param> 		/// <param name="memberInfo">список для заполнения</param> 		/// <param name="fieldInfo">список для заполнения</param> 		/// <param name="metodInfo">список для заполнения</param> 		/// <param name="eventInfo">список для заполнения</param> 		public static void GetInterfaceMetadata(List<Type> interfeceList, ref List<PropertyInfo> propertyInfo, 			ref List<MemberInfo> memberInfo, ref List<FieldInfo> fieldInfo, ref List<MethodInfo> metodInfo, ref List<EventInfo> eventInfo) 		{ 			int count = interfeceList.Count; 			for (int a = 0; a < count; a++) 			{ 				////////////////////////////////////////////////////////////////////////// 				//базовые евенты и проперти 				PropertyInfo[] propertyIE = interfeceList[a].GetProperties(); 				propertyInfo.AddRange(propertyIE); 				EventInfo[] events = interfeceList[a].GetEvents(); 				eventInfo.AddRange(events); 				MemberInfo[] membersIE = interfeceList[a].GetMembers(); 				memberInfo.AddRange(membersIE); 				FieldInfo[] fieldIE = interfeceList[a].GetFields(); 				fieldInfo.AddRange(fieldIE); 				MethodInfo[] metodIE = interfeceList[a].GetMethods(); 				metodInfo.AddRange(metodIE); 			}  		} 		/// <summary> 		/// функция нахождения пвоторяющихся  пропертей 		/// </summary> 		/// <param name="propertyInfoInterface"></param> 		/// <returns></returns> 		public static Dictionary<string, MetodInfoDouble> RemoveDoubleProperty(List<PropertyInfo> propertyInfoInterface) 		{ 			if (propertyInfoInterface == null) 				return null; 			Dictionary<string, MetodInfoDouble> m_doubleList = new Dictionary<string, MetodInfoDouble>(); 			int count = propertyInfoInterface.Count - 1; 			for (int a = count; a >= 0; a--) 			{ 				List<PropertyInfo> fnd = propertyInfoInterface.FindAll( 					(PropertyInfo inf) => inf.Name == propertyInfoInterface[a].Name);  				PropertyInfo fullMetod = null; 				MetodInfoDouble mDouble = new MetodInfoDouble(); 				mDouble.getMetod = null; 				mDouble.setMetod = null;  				if (fnd != null && fnd.Count > 1) 				{ 					string tmp = ""; 					for (int b = 0; b < fnd.Count; b++) 					{ 						tmp += fnd[b].ReflectedType.FullName + "\r\n"; 						propertyInfoInterface.Remove(fnd[b]); 						if (fnd[b].CanRead && fnd[b].CanWrite) 							fullMetod = fnd[b]; 						else if (fnd[b].CanRead) 							mDouble.getMetod = fnd[b].GetGetMethod(); 						else if (fnd[b].CanWrite) 							mDouble.setMetod = fnd[b].GetSetMethod(); 					} #if DEBUG 					//MessageBox.Show("DEBUG:\r\nПовторяющийся параметр с именем: " + fnd[0].Name + "\r\nВ интерфейсах:\r\n" + tmp); #endif  					if (fullMetod != null) 						propertyInfoInterface.Add(fullMetod); 					else 					{ 						m_doubleList.Add(fnd[0].Name, mDouble); 						propertyInfoInterface.Add(fnd[0]); 					} 				}  			}  			return m_doubleList; 		} 		public static bool IsPrimitive(Type t) 		{ 			if (t == null) 				return true; 			if (!t.IsClass && !t.IsInterface || t.IsPrimitive || t.IsEnum || t == typeof(String) || t == typeof(Guid) || t == typeof(DateTime)) 				return true;  			return false; 		} 		/// <summary> 		/// функция получения данных и параметра по имени параметра 		/// </summary> 		/// <param name="propertyName">имя параметра</param> 		///<param name="param">передаваемые параметры</param> 		/// <returns>данные, могут быть null если не найденн параметр или он нулевой</returns> 		static public object GetData(object baseCalss,string propertyName, object[] param = null) 		{ 			if (baseCalss == null || propertyName == null) 				return null; 			object RecalcOb = null; 			PropertyInfo _PropertyDescriptor = SV.Tools.ClassesTools.GetPropertyByName(baseCalss, propertyName.ToString(), ref RecalcOb); 			object v = null; 			if (_PropertyDescriptor != null) 				v = _PropertyDescriptor.GetValue((RecalcOb == null ? baseCalss : RecalcOb), param); 			return v; 		} 		/// <summary> 		/// установка данных в параметр по имени 		/// </summary> 		/// <param name="propertyName">имя параметра</param> 		/// <param name="newPropertyData">новое значение</param> 		///<param name="param">передаваемые параметры</param> 		/// <returns>false - если параметр небыл найден</returns> 		static public bool SetData(object baseCalss, string propertyName, object newPropertyData, object[] param = null) 		{ 			if (baseCalss == null || propertyName == null) 				return false; 			object RecalcOb = null; 			PropertyInfo _PropertyDescriptor = SV.Tools.ClassesTools.GetPropertyByName(baseCalss, propertyName, ref RecalcOb); 			if (_PropertyDescriptor == null) 				return false;  			object data = newPropertyData; 			if (newPropertyData != null && newPropertyData.GetType() != _PropertyDescriptor.PropertyType) 				data = SV.ConversionTools.DataCoversion.ConvertByType(data.ToString(), _PropertyDescriptor.PropertyType); 			_PropertyDescriptor.SetValue((RecalcOb == null ? baseCalss : RecalcOb), data, param); 			return true; 		}   	} } 

Теперь я мог получить полностью всю информацию по любому объекту и

даже отправлять и получать данные в параметры просто по имени параметра

		/// <summary> 		/// функция получения данных и параметра по имени параметра 		/// </summary> 		/// <param name="propertyName">имя параметра</param> 		///<param name="param">передаваемые параметры</param> 		/// <returns>данные, могут быть null если не найденн параметр или он нулевой</returns> 		static public object GetData(object baseCalss,string propertyName, object[] param = null) 		{ 			if (baseCalss == null || propertyName == null) 				return null; 			object RecalcOb = null; 			PropertyInfo _PropertyDescriptor = SV.Tools.ClassesTools.GetPropertyByName(baseCalss, propertyName.ToString(), ref RecalcOb); 			object v = null; 			if (_PropertyDescriptor != null) 				v = _PropertyDescriptor.GetValue((RecalcOb == null ? baseCalss : RecalcOb), param); 			return v; 		} 		/// <summary> 		/// установка данных в параметр по имени 		/// </summary> 		/// <param name="propertyName">имя параметра</param> 		/// <param name="newPropertyData">новое значение</param> 		///<param name="param">передаваемые параметры</param> 		/// <returns>false - если параметр небыл найден</returns> 		static public bool SetData(object baseCalss, string propertyName, object newPropertyData, object[] param = null) 		{ 			if (baseCalss == null || propertyName == null) 				return false; 			object RecalcOb = null; 			PropertyInfo _PropertyDescriptor = SV.Tools.ClassesTools.GetPropertyByName(baseCalss, propertyName, ref RecalcOb); 			if (_PropertyDescriptor == null) 				return false;  			object data = newPropertyData; 			if (newPropertyData != null && newPropertyData.GetType() != _PropertyDescriptor.PropertyType) 				data = SV.ConversionTools.DataCoversion.ConvertByType(data.ToString(), _PropertyDescriptor.PropertyType); 			_PropertyDescriptor.SetValue((RecalcOb == null ? baseCalss : RecalcOb), data, param); 			return true; 		} 

Это был прорыв. Была создана обертка, которая занималась биндингом данных к GUI по тестовым структурам. Скорость разработки значительно увеличилась. В принципе можно было бы успокоиться.
Но я же ленивый.

Шаг третий

Сидя, как то, в пятницу, в баре, в глаза бросилась какая то вывеска-реклама. Что-то там было с словосочетанием ASM… Затуманенный мозг, сразу подбросил ассоциацию: ASM — ASSEMBLER и тут же всплыло воспоминание Common Intermediate Language, а за ним IL Disassembler. Бросив друзей и бар, я побежал домой, не забыв, правда, захватить с собой пару литров пива для стимуляции.

Класс ILGenerator
Дома, почитав информацию по этому классу, я понял — это оно.

Кручу-верчу, что хочу то и ворочу

Собрав в кучу всю информацию, я приступил к делу.

Перво-наперво создав консольный проект с простейшим кодом

 interface iT     {         int i { get; set; }     }     class cT : iT     {         int t = 0;         public int i         {             get             {                 return t;             }             set             {                 t = value;             }         }     }     class Program     {         static void Main(string[] args)         {         }     } 

Я просмотрел во что он разворачивается с помощью Ildasm.exe (IL Disassembler)

ASM code:

.class interface private abstract auto ansi ILG.iT {   .method public hidebysig newslot specialname abstract virtual            instance int32  get_i() cil managed   {   } // end of method iT::get_i    .method public hidebysig newslot specialname abstract virtual            instance void  set_i(int32 'value') cil managed   {   } // end of method iT::set_i    .property instance int32 i()   {     .get instance int32 ILG.iT::get_i()     .set instance void ILG.iT::set_i(int32)   } // end of property iT::i } // end of class ILG.iT  .class private auto ansi beforefieldinit ILG.cT        extends [mscorlib]System.Object        implements ILG.iT {   .field private int32 t   .method public hidebysig newslot specialname virtual final            instance int32  get_i() cil managed   {     //      .maxstack  1     .locals init ([0] int32 V_0)     IL_0000:  nop     IL_0001:  ldarg.0     IL_0002:  ldfld      int32 ILG.cT::t     IL_0007:  stloc.0     IL_0008:  br.s       IL_000a      IL_000a:  ldloc.0     IL_000b:  ret   } // end of method cT::get_i    .method public hidebysig newslot specialname virtual final            instance void  set_i(int32 'value') cil managed   {     //      .maxstack  8     IL_0000:  nop     IL_0001:  ldarg.0     IL_0002:  ldarg.1     IL_0003:  stfld      int32 ILG.cT::t     IL_0008:  ret   } // end of method cT::set_i    .method public hidebysig specialname rtspecialname            instance void  .ctor() cil managed   {     //      .maxstack  8     IL_0000:  ldarg.0     IL_0001:  ldc.i4.0     IL_0002:  stfld      int32 ILG.cT::t     IL_0007:  ldarg.0     IL_0008:  call       instance void [mscorlib]System.Object::.ctor()     IL_000d:  nop     IL_000e:  ret   } // end of method cT::.ctor    .property instance int32 i()   {     .get instance int32 ILG.cT::get_i()     .set instance void ILG.cT::set_i(int32)   } // end of property cT::i } // end of class ILG.cT 

Немножко почесав репу, я создал класс по генерации динамических объектов на базе переданных: базового класса(любого) и списка интерфейсов:

Ссылка на проект на GitHub

Немного опишу проект

Формирование свойств объекта:

GET

	void CreatePropertyGetMetod(PropertyInfo property, PropertyBuilder custNamePropBldr, FieldBuilder fieldProperty, object redirectData) 		{ 			#region GET_METOD 			//находим метод гет для проперти 			MethodInfo inf = property.GetGetMethod(); 			//если такого нет ищем в дублированных 			if (inf == null) 			{ 				if (m_doubleList.ContainsKey(property.Name)) 					inf = m_doubleList[property.Name].getMetod; 			} 			//если метод найден то начинаем его делать 			if (inf != null) 			{ 				//создаем построитель для метода 				MethodBuilder custNameGetPropMthdBldr = 						m_TypeBuilder.DefineMethod("get_" + property.Name, 												m_getSetAttr, 												  property.PropertyType, 												   Type.EmptyTypes); 				//создаем генератор ИЛ 				ILGenerator custNameGetIL = custNameGetPropMthdBldr.GetILGenerator(); 				System.Reflection.Emit.Label end = custNameGetIL.DefineLabel(); 				//начинаем формировать асмокод 				custNameGetIL.Emit(OpCodes.Nop); 				custNameGetIL.Emit(OpCodes.Ldarg_0); 				//возвращаем локальную переменную 				custNameGetIL.Emit(OpCodes.Ldfld, fieldProperty); 				//выход из проперти 				custNameGetIL.Emit(OpCodes.Ret); 				//перезаписываем метод по умолчанию 				m_TypeBuilder.DefineMethodOverride(custNameGetPropMthdBldr, inf); 				//устанавливаем этот метод 				custNamePropBldr.SetGetMethod(custNameGetPropMthdBldr); 			} 			//конец создания ГЕТ метода  			////////////////////////////////////////////////////////////////////////// 			#endregion 		} 

SET

void CreatePropertySetMetod(PropertyInfo property, PropertyBuilder custNamePropBldr, FieldBuilder fieldProperty, object redirectData) 		{ 			#region SET_METOD 			//находим сет метод 			MethodInfo inf = property.GetSetMethod(); 			//если нет то ищем в дублях 			if (inf == null) 			{ 				if (m_doubleList != null && m_doubleList.ContainsKey(property.Name)) 					inf = m_doubleList[property.Name].setMetod; 			} 			if (inf != null) 			{ 				MethodBuilder custNameSetPropMthdBldr = 					m_TypeBuilder.DefineMethod("set_" + property.Name, 											   m_getSetAttr, 											   null, 											   new Type[] { property.PropertyType }); 				ILGenerator custNameSetIL = custNameSetPropMthdBldr.GetILGenerator(); 				//создаем локальную переменную  				custNameSetIL.Emit(OpCodes.Ldarg_0);			 				if (fieldProperty != null) 				{ 					LocalBuilder loc = custNameSetIL.DeclareLocal(property.PropertyType); 					custNameSetIL.Emit(OpCodes.Ldfld, fieldProperty); 					custNameSetIL.Emit(OpCodes.Stloc_0); 					 					custNameSetIL.Emit(OpCodes.Ldarg_0); 					custNameSetIL.Emit(OpCodes.Ldarg_1); 					//присваем значение переменной класса								 					custNameSetIL.Emit(OpCodes.Stfld, fieldProperty); 					if (m_baseClass.GetInterface("iMatryoshkaCall") != null) 					{ 						MethodInfo simpleShow = typeof(iMatryoshkaCall).GetMethod("CallPropertyChange"); 						//CallPropertyChange(string propertyName, object CommandID = null, object oldData = null, object newData = null) 						if (simpleShow != null) 						{ 							custNameSetIL.Emit(OpCodes.Ldarg_0);  							custNameSetIL.Emit(OpCodes.Ldstr, property.Name); 														 							custNameSetIL.Emit(OpCodes.Ldc_I4_0); 							custNameSetIL.Emit(OpCodes.Box, typeof(int));  							custNameSetIL.Emit(OpCodes.Ldloc_0); 							custNameSetIL.Emit(OpCodes.Box, property.PropertyType);  							custNameSetIL.Emit(OpCodes.Ldarg_0); 							custNameSetIL.Emit(OpCodes.Ldfld, fieldProperty); 							custNameSetIL.Emit(OpCodes.Box, property.PropertyType);						 							 							custNameSetIL.Emit(OpCodes.Callvirt, simpleShow); 						} 					} 			 				}	 				custNameSetIL.Emit(OpCodes.Ret); 				custNamePropBldr.SetSetMethod(custNameSetPropMthdBldr); 				m_TypeBuilder.DefineMethodOverride(custNameSetPropMthdBldr, inf); 			} 			#endregion 		} 

Из текущих возможностей:

Формирование динамических классов-объектов на базе переданных: базового класса и списка интерфейсов, возможность сохранить эти объекты в отдельную библиотеку(dll), через BuilderClassesPropertyAttribyte наследника Attribute можно задавать у встроенных параметров-объектов различное наследование и поведение. Формирование и инициализация объектов-классов производиться с множеством вложенных объектов.

Планирую в будущем:

Дать возможность формировать объекты от нескольких классов и интерфейсов.Очень мне уж не хватало после С++ этого.
ссылка на оригинал статьи https://habrahabr.ru/post/316238/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *