【C#编程辅助】可动态配置的函数参数调用实现

源起:

    经常使用编写单元测试,经常更改函数参数的传值,通过ParamsBuild配合配置文件配置就可以做到函数参数值可配置。

因参数经常是一段有效的数值区间,于是添加了参数的区间、补充、格式化等功能。

方便缩写的命名空间导入
1using System;
2using System.Xml.Serialization;
3using System.Text.RegularExpressions;
4using System.Collections.Generic;
5using System.Diagnostics;
6using System.Reflection;
7using System.Xml;
第一部分:参数构建器(主题设计)
代码ParamsBuild
    /// <summary>
    
/// 参数构建器
    
/// </summary>
    [Serializable]
    
public class ParamsBuild
    {
        
/// <summary>
        
/// 获取或设置方法参数配置集合
        
/// </summary>
        
/// <value>The param config.</value>
        [XmlElement(ElementName = "Config")]
        
public MethodParams[] ParamConfig { getset; }

        
/// <summary>
        
/// 获取指定类型名称的方法参数
        
/// </summary>
        
/// <param name="typeFullName">类型全称</param>
        
/// <param name="methodName">方法名称</param>
        
/// <returns></returns>
        public MethodParams GetParamConfig(string typeFullName, string methodName)
        {
            MethodParams result 
= null;
            
foreach (MethodParams pm in ParamConfig)
            {
                
if (pm.TypeFullName == typeFullName && pm.MethodName == methodName)
                {
                    result 
= pm;
                    
break;
                }
            }
            
return result;
        }

        
/// <summary>
        
/// 获取当前调用方法的方法参数配置
        
/// </summary>
        
/// <returns></returns>
        public MethodParams GetParamConfig()
        {
            MethodBase method 
= new System.Diagnostics.StackFrame(1).GetMethod();
            
return GetParamConfig(method.DeclaringType.FullName, method.Name);
        }

        
/// <summary>
        
/// 获取同类型中其他方法名的参数配置
        
/// </summary>
        
/// <param name="methodName">方法名称</param>
        
/// <returns></returns>
        public MethodParams GetParamConfig(string methodName)
        {
            MethodBase method 
= new System.Diagnostics.StackFrame(1).GetMethod();
            
return GetParamConfig(method.DeclaringType.FullName, methodName);
        }

        
private static ParamsBuild _instance;
        
/// <summary>
        
/// 获取配置文件中的实例
        
/// </summary>
        public static ParamsBuild ConfigInstance
        {
            
get
            {
                
if (_instance == null)
                {
                    _instance 
= XmlSerializeSectionHandler.GetObject<ParamsBuild>("ParamsBuild");
                }
                
return _instance;
            }
        }
    }


第二部分:方法参数配置

MethodParams
    /// <summary>
    
/// 方法参数配置
    
/// </summary>
    [Serializable]
    
public class MethodParams : ICloneable
    {
        
/// <summary>
        
/// 类型全称
        
/// </summary>
        [XmlAttribute(AttributeName = "type")]
        
public string TypeFullName { getset; }

        
/// <summary>
        
/// 方法名
        
/// </summary>
        [XmlAttribute(AttributeName = "method")]
        
public string MethodName { getset; }

        
/// <summary>
        
/// 参数集合
        
/// </summary>
        public Param[] Params { getset; }

        
/// <summary>
        
/// 获取参数集合中特定名称的参数
        
/// </summary>
        
/// <param name="paramName">参数名称</param>
        
/// <returns></returns>
        public Param GetParam(string paramName)
        {
            Param p 
= null;
            
if (Params != null && Params.Length > 0)
            {
                
foreach (Param pi in Params)
                {
                    
if (pi.Name == paramName)
                    {
                        p 
= pi;
                        
break;
                    }
                }
            }
            
return p;
        }

        
/// <summary>
        
/// [穷举]获取所有方法最个可能参数的的情况(各个参数可能情况的乘积)
        
/// </summary>
        
/// <returns></returns>
        public MethodParams[] GetAvailableMethodParams()
        {
            List
<MethodParams> mparmList = new List<MethodParams>();
            
bool addDefaultMethodParams = false;
            List
<Param> baseParams = new List<Param>();
            
foreach (Param pm in Params)
            {
                
foreach (Param rpm in Params)
                {
                    
if (rpm.Name == pm.Name)
                    {
                        
#region 当前参数
                        
int totalCount = rpm.GetUsableValueCount();
                        
//Console.WriteLine(rpm.Name + " : " + totalCount);
                        if (totalCount == 1)
                        {
                            
if (rpm.IsOptional) baseParams.Add(rpm);
                            
if (!addDefaultMethodParams)
                            {
                                MethodParams defaultMethodParams 
= new MethodParams();
                                defaultMethodParams.TypeFullName 
= TypeFullName;
                                defaultMethodParams.MethodName 
= MethodName;
                                defaultMethodParams.Params 
= Params;
                                mparmList.Add(defaultMethodParams);

                                addDefaultMethodParams 
= true;
                            }
                        }
                        
else
                        {
                            
#region 穷举参数的所有可能值清况
                            List
<Param> rpmAvailableList = new List<Param>();
                            
foreach (Param rpmAvailable in Params)
                            {
                                
if (rpmAvailable.Name != rpm.Name)
                                {
                                    rpmAvailableList.Add(rpmAvailable);
                                }
                            }
                            rpmAvailableList.Add(
null);

                            
int t = mparmList.Count;
                            
//Console.WriteLine("Available Count:" + t);
                            for (int i = 0; i < totalCount; i++)
                            {

                                Param parmAvailale 
= (Param)rpm.Clone();
                                parmAvailale.Value 
= rpm.GetUsableValue(i).ToString();
                                rpmAvailableList[rpmAvailableList.Count 
- 1= parmAvailale;

                                
//小于一个单位
                                if (t < 1)
                                {
                                    
#region 基数为1单位时,加法即乘法。
                                    MethodParams mparmAvailale 
= new MethodParams();
                                    mparmAvailale.TypeFullName 
= TypeFullName;
                                    mparmAvailale.MethodName 
= MethodName;
                                    mparmAvailale.Params 
= rpmAvailableList.ToArray();
                                    
//Console.WriteLine("< 1 Unit,ADD:---" + parmAvailale.Value);
                                    mparmList.Add(mparmAvailale);
                                    
#endregion
                                }
                                
else
                                {
                                    
for (int k = 0; k < t; k++)
                                    {
                                        
#region 乘以基数运算
                                        MethodParams mparmInList 
= mparmList[k];
                                        
//Console.WriteLine(k);

                                        
bool blnAddNewMethodParam = true;

                                        
#region 检查是否有重复的方法参数定义
                                        
foreach (Param parminList in mparmInList.Params)
                                        {
                                            
if (parminList.Name == rpm.Name)
                                            {
                                                
if (parminList.Value == parmAvailale.Value)
                                                {
                                                    blnAddNewMethodParam 
= false;
                                                    
//Console.WriteLine("Not ADD:" + parmAvailale.Value);
                                                    break;
                                                }
                                            }
                                        }
                                        
#endregion

                                        
if (blnAddNewMethodParam == true)
                                        {
                                            Param[] upataParams 
= new Param[mparmInList.Params.Length];
                                            
#region DeepClone
                                            
for (int u = 0; u < upataParams.Length; u++)
                                            {
                                                Param pu 
= (Param)mparmInList.Params[u].Clone();
                                                upataParams[u] 
= pu;
                                            }
                                            
#endregion

                                            
#region Update
                                            
int idx = 0;
                                            
foreach (Param parminListUpdate in mparmInList.Params)
                                            {
                                                
if (parminListUpdate.Name == rpm.Name)
                                                {
                                                    upataParams[idx].Value 
= parmAvailale.Value;
                                                    
break;
                                                }
                                                idx
++;
                                            }
                                            
#endregion

                                            MethodParams mparmNew 
= new MethodParams();
                                            mparmNew.TypeFullName 
= TypeFullName;
                                            mparmNew.MethodName 
= MethodName;
                                            mparmNew.Params 
= upataParams;
                                            
//Console.WriteLine("Update:---" + parmAvailale.Value);
                                            mparmList.Add(mparmNew);
                                        }
                                        
#endregion
                                    }
                                    
//Console.WriteLine("-----" + t + "*" + (i+1).ToString());
                                }
                            }
                            
#endregion
                        }

                        
if (rpm.IsOptional)
                        {
                            
#region 该参数可选情况
                            
if (mparmList.Count < 1)
                            {
                                List
<Param> rpmOptionList = new List<Param>();
                                
foreach (Param rpmOpt in Params)
                                {
                                    
if (rpmOpt.Name != rpm.Name)
                                    {
                                        rpmOptionList.Add(rpmOpt);
                                    }
                                }

                                MethodParams mparmOpt 
= new MethodParams();
                                mparmOpt.TypeFullName 
= TypeFullName;
                                mparmOpt.MethodName 
= MethodName;
                                mparmOpt.Params 
= rpmOptionList.ToArray();
                                mparmList.Add(mparmOpt);
                            }
                            
else
                            {
                                
#region X2 可选操作
                                
for (int ei = 0; ei < mparmList.Count; ei++)
                                {
                                    List
<Param> eiOptionList = new List<Param>();
                                    
foreach (Param rpmOpt in mparmList[ei].Params)
                                    {
                                        
if (rpmOpt.Name != rpm.Name) eiOptionList.Add(rpmOpt);
                                    }

                                    Param[] eiToAddArray 
= eiOptionList.ToArray();
                                    
if (!ExistsMethodParams(mparmList, eiToAddArray))
                                    {
                                        MethodParams eiParmOpt 
= (MethodParams)mparmList[ei].Clone();
                                        eiParmOpt.Params 
= eiToAddArray;
                                        mparmList.Add(eiParmOpt);
                                    }
                                }
                                
#endregion
                            }
                            
#endregion
                        }
                        
#endregion
                    }
                }
            }

            
if (baseParams.Count == Params.Length)
            {
                MethodParams baseMethodParams 
= new MethodParams();
                baseMethodParams.TypeFullName 
= TypeFullName;
                baseMethodParams.MethodName 
= MethodName;
                baseMethodParams.Params 
= baseParams.ToArray();
                mparmList.Add(baseMethodParams);
            }
            
return mparmList.ToArray();
        }

        
/// <summary>
        
/// 检查所有的参数集合中是否存在指定的方法参数
        
/// </summary>
        private bool ExistsMethodParams(List<MethodParams> plist, Param[] MethodParamsArray)
        {
            
bool exists = false;
            
for (int ichk = 0; ichk < plist.Count; ichk++)
            {
                Param[] toCHKArray 
= plist[ichk].Params;

                
//参数个数不匹配
                if (toCHKArray.Length != MethodParamsArray.Length)
                {
                    
continue;
                }
                
else
                {
                    
int eqTotal = 0;
                    
foreach (Param chkParam in toCHKArray)
                    {
                        
for (int eic = 0; eic < MethodParamsArray.Length; eic++)
                        {
                            
if (chkParam.Name == MethodParamsArray[eic].Name
                                
&& chkParam.Value == MethodParamsArray[eic].Value)
                            {
                                eqTotal
++;
                            }
                        }
                    }

                    
if (eqTotal == MethodParamsArray.Length)
                    {
                        exists 
= true;
                        
break;
                    }
                }

            }
            
return exists;
        }

        
#region ICloneable 成员

        
/// <summary>
        
/// 创建作为当前实例副本的新对象。
        
/// </summary>
        
/// <returns>作为此实例副本的新对象。</returns>
        public object Clone()
        {
            MethodParams m 
= new MethodParams();
            m.TypeFullName 
= TypeFullName;
            m.MethodName 
= MethodName;
            Param[] mParams 
= new Param[Params.Length];
            
for (int i = 0; i < mParams.Length; i++)
            {
                mParams[i] 
= (Param)Params[i].Clone();
            }
            m.Params 
= mParams;
            
return m;
        }

        
#endregion
    }


 第三部分:测试参数

代码Param
    /// <summary>
    
/// 测试参数
    
/// </summary>
    [Serializable]
    
public class Param : ICloneable
    {
        
/// <summary>
        
/// 参数名称
        
/// </summary>
        [XmlAttribute(AttributeName = "name")]
        
public string Name { getset; }

        
/// <summary>
        
/// 参数类型名称
        
/// </summary>
        [XmlAttribute(AttributeName = "type")]
        
public string ParamType { getset; }

        
/// <summary>
        
/// 参数的值
        
/// </summary>
        public string Value { getset; }

        
/// <summary>
        
/// 参数默认值
        
/// </summary>
        public string Default { getset; }

        
/// <summary>
        
/// 是否是可选参数
        
/// </summary>
        [XmlAttribute(AttributeName = "optional")]
        
public bool IsOptional { getset; }

        
/// <summary>
        
/// 其他可使用的测试数据,默认使用英文逗号分隔各个值。
        
/// <para>支持区间语法:</para>
        
/// <para>1.普通数字区间:1-100[;1] 即1-100的数字,1为步长。</para>
        
/// <para>2.普通字母区间:a-z[;1] 即a-z的字符,1为步长,结果为a,b,c,d,e,f...z。</para>
        
/// <para>3.带小数的区间:0.00-1.00[;0.01] 即0-1之间的两位小数,0.01为步长。</para>
        
/// <para>4.完整语法定义:开始-结束[;步长][填充模式,填充长度(前面至0)],[]内参数可选,默认步长为1个单位。
        
/// 形如:UsableValues = "125.00-124.00;0.01;[url?down-{0}.html,5]"</para>
        
/// </summary>
        public string UsableValues { getset; }

        
/// <summary>
        
/// 其他可使用值的分隔符
        
/// </summary>
        [XmlAttribute(AttributeName = "split")]
        
public string UsableValueSplit { getset; }

        

        
/// <summary>
        
/// 获取参数的强类型值
        
/// </summary>
        
/// <returns></returns>
        public object GetValue()
        {
            
if (Value == null)
            {
                
if (Default != null) Value = Default;
            }
            
object result = Value;
            
if (this.ParamType != null)
            {
                
//XML反序列化获取参数的值
                Type pType = Type.GetType(ParamType);
                
if (SharedGlobal.HasAttribute(pType, typeof(System.SerializableAttribute), true))
                {
                    XmlDocument xmlDoc 
= new XmlDocument();
                    xmlDoc.LoadXml(Value.Trim());
                    MethodInfo fun 
= typeof(SharedGlobal).GetMethod("GetObject"new Type[] { typeof(XmlDocument) });
                    fun 
= fun.MakeGenericMethod(pType);
                    
return fun.Invoke(nullnew object[] { xmlDoc });
                }
                
else
                {
                    result 
= Convert.ChangeType(result, pType);
                }
            }
            
return result;
        }

        
/// <summary>
        
/// 其他可选参数值集合
        
/// </summary>
        private object[] OtherUsableValueCollection = new Object[0];

        
private void BuildOtherUsableValues(string otherValueConfig, string split)
        {
            
if (string.IsNullOrEmpty(split)) split = ",";
            
if (split != "-")
            {
                OtherUsableValueCollection 
= Regex.Split(otherValueConfig, Regex.Escape(split));
            }
            
else
            {
                
// (\-?[\d\.a-zA-Z]+)\s?\-\s?(\-?[\d\.a-zA-Z]+)(;(\-?[\d\.]+))?(;\[(.+),(\d+)\])?
                
// (\\-?[\\d\\.a-zA-Z]+)\\s?\\-\\s?(\\-?[\\d\\.a-zA-Z]+)(;(\\-?[\\d\\.]+))?(;\\[(.+),(\\d+)\\])?
                
// 1.区间开始 2.区间结束 4.步长,默认为1个单位  6.替换模型 7.匹配位数,不足填0补齐

                
string numPattern = "(\\-?[\\d\\.a-zA-Z]+)\\s?\\-\\s?(\\-?[\\d\\.a-zA-Z]+)(;(\\-?[\\d\\.]+))?(;\\[(.+),(\\d+)\\])?";
                Match m 
= Regex.Match(otherValueConfig, numPattern);
                
if (!m.Success)
                {
                    
throw new System.Configuration.ConfigurationErrorsException("对于可用值的区间配置(-)必须指定开始和结束数字或字母");
                }
                
else
                {
                    
//区间范围
                    string[] rangDat = new string[] { m.Groups[1].Value, m.Groups[2].Value };

                    
//默认步长为1个单位,填充位数为1
                    double step = 1.00;
                    
int fillLen = 1;
                    
bool cusStepSet = false, restore2Char = false;
                    
string fillMode = string.Empty;

                    
#region 自动构建数字区间
                    
if (m.Groups.Count == 8)
                    {
                        
//定义了步长 m.Groups[4].Value
                        if (!string.IsNullOrEmpty(m.Groups[4].Value))
                        {
                            step 
= Convert.ToDouble(m.Groups[4].Value);
                            cusStepSet 
= true;
                        }

                        
//定义了替换模型和匹配补齐规则 m.Groups[6].Value 和 m.Groups[7].Value
                        if (!string.IsNullOrEmpty(m.Groups[6].Value) &&
                            
!string.IsNullOrEmpty(m.Groups[7].Value))
                        {
                            fillMode 
= m.Groups[6].Value;
                            fillLen 
= int.Parse(m.Groups[7].Value);
                        }
                    }
                    
#endregion

                    
bool isDouble = false;
                    
int numLen = 0;
                    
double rangeBegin, rangeEnd;

                    
#region 设置循环区间与步长
                    
if (Char.IsLetter(rangDat[0], 0))
                    {
                        rangeBegin 
= Convert.ToDouble((int)rangDat[0][0]);
                        rangeEnd 
= Convert.ToDouble((int)rangDat[1][0]);
                        restore2Char 
= true;
                    }
                    
else
                    {
                        
if (rangDat[0].Contains(".")) isDouble = true;
                        rangeBegin 
= double.Parse(rangDat[0]);
                        rangeEnd 
= double.Parse(rangDat[1]);
                    }

                    
if (isDouble)
                    {
                        numLen 
= Regex.Match(rangDat[0], "\\.(\\d+)$").Groups[1].Value.Length;
                        
if (!cusStepSet)
                        {
                            step 
= 1.00 / Math.Pow(10.00, Convert.ToDouble(numLen));
                        }
                    }
                    
#endregion

                    
//递减
                    if (rangeBegin.CompareTo(rangeEnd) > 0 && step > 0.00) step = 0.00 - step;

                    Func
<bool> fFlag = new Func<bool>(delegate() { return rangeBegin <= rangeEnd; });
                    
//是否递减
                    if (step < 0.00)
                    {
                        fFlag 
= new Func<bool>(delegate() { return rangeBegin >= rangeEnd; });
                    }

                    
string strTemp = string.Empty;
                    List
<string> strValues = new List<string>();
                    
while (fFlag())
                    {
                        
#region 循环
                        
if (restore2Char == true)
                        {
                            
if (fillMode != string.Empty)
                            {
                                strTemp 
= String.Format(fillMode, ((char)((int)rangeBegin)).ToString().PadLeft(fillLen, '0'));
                            }
                            
else
                            {
                                strTemp 
= ((char)((int)rangeBegin)).ToString();
                            }
                        }
                        
else
                        {
                            strTemp 
= (isDouble) ? rangeBegin.ToString("N" + numLen) : rangeBegin.ToString();
                            
if (fillMode != string.Empty)
                            {
                                strTemp 
= String.Format(fillMode, strTemp.PadLeft(fillLen, '0'));
                            }
                        }

                        
//Console.WriteLine(strTemp);
                        strValues.Add(strTemp);
                        rangeBegin 
+= step;

                        
#endregion
                    }
                    OtherUsableValueCollection 
= strValues.ToArray();
                }
            }
        }

        
/// <summary>
        
/// 获取可用参数值的个数
        
/// </summary>
        
/// <returns></returns>
        public int GetUsableValueCount()
        {
            
if (UsableValues == nullreturn 1;
            
if (OtherUsableValueCollection.Length == 0)
            {
                BuildOtherUsableValues(UsableValues, UsableValueSplit 
?? ",");
            }

            
//其他可用和默认值
            return OtherUsableValueCollection.Length + 1;
        }

        
/// <summary>
        
/// 获取可用第N个参数
        
/// </summary>
        
/// <param name="idx">可选的参数索引</param>
        
/// <returns></returns>
        public object GetUsableValue(int idx)
        {
            
object result = null;
            
int totalCount = GetUsableValueCount();
            
if (totalCount > 0 && idx < totalCount)
            {
                
if (idx == 0)
                {
                    result 
= GetValue();
                }
                
else
                {
                    result 
= OtherUsableValueCollection[idx - 1];
                    
if (this.ParamType != null)
                    {
                        result 
= Convert.ChangeType(result, Type.GetType(ParamType));
                    }
                }
            }
            
return result;
        }

        
/// <summary>
        
/// 获取当前参数的值定义的字符串表示
        
/// </summary>
        public override string ToString()
        {
            
return string.Concat("{"string.Format("name:{0},type:{1},Value:{2},optional:{3},Default:{4},UsableValues:{5},split:{6}",
                Name,
                ParamType 
?? "string",
                Value,
                IsOptional,
                Default 
?? "null",
                UsableValues 
?? "null",
                UsableValueSplit 
?? "\",\""),
                
"}");
        }


        
#region ICloneable 成员

        
/// <summary>
        
/// 创建作为当前实例副本的新对象。
        
/// </summary>
        
/// <returns>作为此实例副本的新对象。</returns>
        public object Clone()
        {
            Param p 
= new Param();
            p.Name 
= Name;
            p.ParamType 
= ParamType;
            p.Value 
= Value;
            p.Default 
= Default;
            p.IsOptional 
= IsOptional;
            p.UsableValues 
= UsableValues;
            p.UsableValueSplit 
= UsableValueSplit;
            
return p;
        }

        
#endregion
    }


 使用示例:

代码CollectExample
 static void CollectExample()
        {
            MethodParams mParams 
= ParamsBuild.ConfigInstance.GetParamConfig();
            MethodParams[] allParams 
= mParams.GetAvailableMethodParams();

            MethodParams cParams 
= null;
            
string rawRaw = string.Empty;
            
string currentURL = string.Empty;

            
using (WebClient client = new WebClient())
            {
                client.Encoding 
= Encoding.Default;
                client.Headers.Add(HttpRequestHeader.UserAgent, 
"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; InfoPath.2; .NET4.0C; .NET4.0E)");

                
for (int i = 0, j = allParams.Length; i < j; i++)
                {
                    cParams 
= allParams[i];
                    currentURL 
= string.Format(cParams.GetParam("url").Value, cParams.GetParam("id").Value);

                    Console.WriteLine(currentURL);
                    
try
                    {
                        
//下载资源
                        
//rawRaw = client.DownloadString(currentURL);
                    }
                    
catch (Exception exp)
                    {
                        Console.WriteLine(exp.Message);
                    }

                    System.Threading.Thread.Sleep(
100);

                    
if (i % 5 == 0)
                    {
                        System.Threading.Thread.Sleep(
1000);
                    }
                }

            }
        }


 使用场景:一般可用于单元测试、数据采集等代码片段中,去年编写的博客草稿,欢迎各位拍砖~

 

完整及演示代码下载:https://files.cnblogs.com/ridge/ParamsBuildTest.zip

posted @ 2010-04-28 10:32  Ridge Wong  阅读(1719)  评论(0)    收藏  举报