Небольшой опыт, полученный благодаря лени
Года три назад, работал я на одну фирму. Было нас 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… Затуманенный
Класс 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 можно задавать у встроенных параметров-объектов различное наследование и поведение. Формирование и инициализация объектов-классов производиться с множеством вложенных объектов.
Планирую в будущем:
Дать возможность формировать объекты от нескольких классов и интерфейсов.Очень мне уж не хватало после С++ этого.
Автор: singlevolk