Небольшой опыт, полученный благодаря лени
Года три назад, работал я на одну фирму. Было нас 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)
.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
Немножко почесав репу, я создал класс по генерации динамических объектов на базе переданных: базового класса(любого) и списка интерфейсов:
Немного опишу проект
Формирование свойств объекта:
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 }
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/
Добавить комментарий