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

в 15:53, , рубрики: .net, C#, DynamicAssembly, ILGenerator, PropertyInfo, reflection, Программирование

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

Года три назад, работал я на одну фирму. Было нас 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 = "tt";
        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) + ", IDisposablernt{rn";
            sources += GetConstructor();
            sources += GetProperty();
            sources += GetInitFunction();
            sources += GetSaveFunction();
            sources += GetDeleteFunction();
            sources += GetDisposable();
            sources += StaticGetList();
            sources += GetLastError();
            sources += "t}";
            return true;
        }
        string GetLastError()
        {
            return "#region Errorrn" +
                "        string mError;rn" +
                "        public string lastErrorrn" +
                "        {rn" +
                "            get { return mError; }rn" +
                "        }rn" +
                "#endregionrn";
        }
        string StaticGetList()
        {
            return "#region Static_GetListrn" +
                   "     public static List<"+className+"> GetList(string SQL_WHERE = "")rn" +
                   "     {rn" +
                   "         List<" + className + "> lst = null;rn" +
                   "         DataBaseWorkBase db = new DataBaseWorkBase(v2_SacuraConstants.ConnectionString);rn" +
                   "         if (db.Open())rn" +
                   "         {rn" +
                   "             lst = new List<" + className + ">();rn" +
                   "             SqlCommand sqlcmd = db.CreateSQLCommand();rn" +
                   "             sqlcmd.CommandText = "SELECT * " +rn" +
                   "                 "FROM "+tablename+" " + SQL_WHERE;rn" +
                   "             SqlDataReader reader = sqlcmd.ExecuteReader();rn" +
                   "             while (reader.Read())rn" +
                   "             {rn" +
                   "                 " + className + " ord = new " + className + "();rn" +
                   "                 if (ord.InitFromDataReader(reader))rn" +
                   "                     lst.Add(ord);rn" +
                   "             }rn" +
                   "             reader.Close();rn" +
                   "             reader = null;rn" +
                   "         }rn" +
                   "         db.Close();rn" +
                   "         db = null;rn" +
                   "         return lst;rn" +
                   "     }rn" +
                "#endregionrn";
        }
        string GetDisposable()
        {
            return "#region Члены IDisposablern" +
                   "     public override void Close()rn" +
                   "     {rn" +
                   "         base.Close();rn" +
                   "     }rn" +
                   "rn" +
                   "    protected override void Dispose(bool disposing)rn" +
                   "    {rn" +
                   "        if (disposing)rn" +
                   "        {rn"+
                   "            Close();rn" +
                   "            base.Dispose(true);rn" +
                   "        }rn" +
                   "    }rn" +                  
                   "#endregionrn";
        }
        string GetDeleteFunction()
        {
            string con = "#region Deletern"+            
                tab+"public bool Delete()rn"+ 
                tab+"{rn"+ 
                tab+"    bool result = false;rn"+ 
                tab+"    tryrn"+ 
                tab+"    {rn"+ 
                tab+"        SqlCommand sqlcmd = CreateSQLCommand();rn"+ 
                tab+"        sqlcmd.CommandText = "DELETE FROM "+tablename+" WHERE ID=@ID";rn"+ 
                tab+"        sqlcmd.Parameters.AddWithValue("@ID", m_ID);rn"+ 
                tab+"        sqlcmd.ExecuteNonQuery();rn"+ 
                tab+"        result = true;rn"+ 
                tab+"    }rn"+ 
                tab+"    catch (System.Exception ex)rn"+ 
                tab+"    {rn"+ 
                tab+"    }rn"+ 
                tab+"    return result;rn"+ 
                tab+"}rn"+ 
                "#endregionrn";
            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 + ");rn";
                else
                    pr += tab + tab + mListColumn[a].Name +"=reader[" + a.ToString() + "].ToString();rn";
            }          
            return pr;
        }
        string GetInitFunction()
        {
            string fn = "#region Initrn" +
                    tab + "public bool InitFromDataReader(SqlDataReader reader)rn" +
                    tab + "{rn" +
                    tab + "    tryrn" +
                    tab + "    {rn" +
                    GetInitParams() +
                    tab + "    }rn" +
                    tab + "    catch (System.Exception ex)rn" +
                    tab + "    {rn" +
                    tab + "        return false;rn" +
                    tab + "    }rn" +
                    tab + "    return true;rn" +
                    tab + "}rn" +
                    tab + "public bool Init(string id)rn" +
                    tab + "{rn" +
                    tab + "    if (string.IsNullOrEmpty(id))rn" +
                    tab + "        return false;rn" +
                    tab + "    int t;rn" +
                    tab + "    int.TryParse(id, out t);rn" +
                    tab + "    return Init(t);rn" +
                    tab + "}rn" +
                    tab + "public bool Init(int id)rn" +
                    tab + "{rn" +
                    tab + "   if (!base.Open())rn" +
                    tab + "       return false;rn" +
                    tab + "   bool IsLoad = false;rn" +
                    tab + "   tryrn" +
                    tab + "   {rn" +
                    tab + "       SqlCommand sqlcmd = CreateSQLCommand();rn" +
                    tab + "       sqlcmd.CommandText = "SELECT * " +rn" +
                    tab + "           "FROM " + tablename + " WHERE [ID]=@ID";rn" +
                    tab + "       sqlcmd.Parameters.AddWithValue("@ID", id);rn" +
                    tab + "       SqlDataReader reader = sqlcmd.ExecuteReader();rn" +
                    tab + "       if (reader.Read())rn" +
                    tab + "       {rn" +
                    tab + "          if (!InitFromDataReader(reader))rn" +
                    tab + "            {rn" +
                    tab + "              reader.Close();rn" +
                    tab + "              base.Close();rn"+
                    tab + "              return false;rn" +
                    tab + "            }rn" +
                    tab + "           IsLoad = true;rn" +
                    tab + "       }rn" +
                    tab + "       reader.Close();rn" +
                    tab + "   }rn" +
                    tab + "   catch (System.Exception ex)rn" +
                    tab + "   {rn" +
                    tab + "       mError = ex.Message;rn" +
                    tab + "       IsLoad = false;rn" +
                    tab + "   }rn" +
                    tab + "   base.Close();" +
                    tab + "   return IsLoad;rn" +
                    tab + "}rn";
            fn += "#endregionrn";
            return fn;
        }
        string GetConstructor()
        {
            string con = "#region Constructorrn" +
                tab + "public " + className + "(string ConnectionString)rn" +
                tab + "t: base (ConnectionString)rn" +
                tab + "{rn" +
                tab + "}rn" +
                tab + "public " + className + "()rn" +
                tab + "t:base(v2_SacuraConstants.ConnectionString)rn" +
                tab + "{rn" +
                tab + "}rn" +
                "#endregionrn";
            return con;
        }
        string GetProperty()
        {
            mListColumn.Clear();
            string src = "#region Propertyrn";
            //////////////////////////////////////////////////////////////////////////
            //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 = "ttt";
                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+ ";rn";
                src += "tt";
                src += "public "+clTp.Type + " " + clTp.Name + "rn" + tab + "{rn" +
                    tab1 + "getrn" + tab1 + "{rn" + tab1 + "treturn m_" + clTp.Name + ";rn" + tab1 + "}rn" + tab1 +
                    "setrn" + tab1 + "{rn" + tab1 + "tm_" + clTp.Name + "=value;rn" + tab1 + "}rn" + tab + "}rn";
            }
            //////////////////////////////////////////////////////////////////////////
            return src + "#endregionrn";
        }
        string GetSaveInsertParams()
        {
            string pr = "";
            int cnt = mListColumn.Count;
            for (int a = 1; a < cnt; a++)
            {
                pr += tab + tab + tab + (a == 1 ? ""[" : "",[") + mListColumn[a].Name + "]"+rn";
            }
            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 ? ""+rn" : ")"");
            }
            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 ? "+rn" : "");
            }
            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+");rn";
            }
            return pr;
        }
        string GetSaveFunction()
        {
            string con = "#region Savern" +
            tab + "public bool Save()rn" +
            tab + "    {rn" +
            tab + "        bool result = false;rn" +
            tab + "        tryrn" +
            tab + "        {rn" +
            tab + "            SqlCommand sqlcmd = CreateSQLCommand();rn" +
            tab + "            if (m_ID <= 0)rn" +
            tab + "            {rn" +
            tab + "                sqlcmd.CommandText = "INSERT INTO " + tablename + " (  "+rn" +
                    GetSaveInsertParams() +
            tab + "                     ") VALUES ("+rn" +
                    GetSaveInsertValues() + "+";SELECT CAST(scope_identity() AS int)";rn" +
            tab + "            }rn" +
            tab + "            elsern" +
            tab + "            {rn" +
            tab + "                sqlcmd.CommandText = "UPDATE " + tablename + " SET " +rn" +
                    GetSaveUpdateParams() + "+rn" +
            tab + "                " WHERE ID=@ID";rn" +
            tab + "                sqlcmd.Parameters.AddWithValue("@ID", m_ID);rn" +
            tab + "            }rn" +
            tab + GetAddWithValue() + "rn" +
            tab + "            if (m_ID > 0)rn" +
            tab + "                sqlcmd.ExecuteNonQuery();rn" +
            tab + "            elsern" +
            tab + "            {rn" +
            tab + "                object ob;rn" +
            tab + "                ob = sqlcmd.ExecuteScalar();rn" +
            tab + "                if(ob != null)rn" +
            tab + "                    int.TryParse(ob.ToString(), out m_ID);rn" +
            tab + "            }rn" +
            tab + "        }rn" +
            tab + "        catch (System.Exception ex)rn" +
            tab + "        {rn" +
            tab + "            mError = ex.Message;rn" +
            tab + "            result = false;rn" +
            tab + "        }rn" +
            tab + "        return result;rn" +
            tab + "    }rn" +
            "#endregionrn";
            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 + "rn";
						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:rnПовторяющийся параметр с именем: " + fnd[0].Name + "rnВ интерфейсах:rn" + 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 можно задавать у встроенных параметров-объектов различное наследование и поведение. Формирование и инициализация объектов-классов производиться с множеством вложенных объектов.

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

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

Автор: singlevolk

Источник

Поделиться новостью

* - обязательные к заполнению поля