泛型和泛型方法扩展

泛型,顾名思义就是指一种抽象意义的数据类型,它满足一定的类型约束条件,比如必须要有无参的构造函数,或者必须继承某些接口,等等。如果说接口是对类成员的定义与声明的话,那么泛型是对类型声明方式的声明和约束,这些抽象的数据类型可以代表符合条件的多种类型的数据,这些类型有共同的处理逻辑。

比如对于系统标准的泛型List<T>,其中T代表任意的数据类型,List规定了可以同时存放多个T类型的数据,并且这些数据是有序排列的,List规定了T的访问方式,比如说List<T>,Add(T obj)代表向List添加一个孩子元素,后添加的元素序号大于前添加的元素,List实现了一套元素访问机制,可以对加入自己的T类型孩子进行增删改查,比如List<T>.RemoveAt(int i index)代表删掉加入的第i个孩子,List可以对队列中间任意一个位置插入一个元素等等。

List<K> List<T>,Join(List<U> b,Func<T,object> a,Func<U,obj> b,Func<T,U,K> c)

代表一种泛型方法,他实现了两个List<T> ,List<U>的关联方式,参数类型Func<T,object>代表一个以T类型数据作为输入,输出object类型的方法,系统将a和b这两个参数的取值逻辑委托给调用方,让调用方确定列表T和U返回的关联值,系统判断如果这两个值相等,就把T类型的数据行和U类型的数据行关联起来,最后一个参数c,也是一个委托,它将T数据和U数据作为数据,让调用方确定需要返回什么,也就是说K类型是调用方确定的,然后系统最终返回一个List<K>,调用方完全不需要知道List<T>和List<U>是如何关联的,只需要调用方输入必要的声明信息,系统自动处理。

结合这种特性,我们可以开发自定义的泛型。

下面举一个例子,说说使用泛型比不使用泛型的优势

假设我要通过一个地址去获取服务器数据,服务器返回一串结构化文本,我通过正则表达式去匹配我需要的数据然后返回一个List列表

常规的写法:

获取基金抬头数据

   public static IEnumerable<Fund> GetFund(string findstring = null)
    {
      string resultstr = Util.HttpCommon.HttpGet(@"http://fund.eastmoney.com/js/fundcode_search.js?v=" + DateTime.Now.ToString("yyyyMMddHHmmss"));
      //Regex regex = new Regex("var r = (?<Data>\\[\\[\"(?<Code>[0-9]{6})\",\".*?\",\"(?<Name>.*?)\",\"(?<Type>.*?)\".*?\\],{0,1})*\\]");
      Regex regex = new Regex("var r = (?<Data>\\[(\\[\"(?<Code>[0-9]{6})\"\\,\".*?\"\\,\"(?<Name>.*?)\"\\,\"(?<Type>.*?)\".*?\\][\\,]{0,1})+\\])");
      Match match = regex.Match(resultstr);
      //int matchSize = match.Groups["Code"].Captures.Count;

      IEnumerable<Fund> s = match.Groups["Code"].Captures.Cast<Capture>().Select((v, i) => new { value = v.Value, index = i }).Join(match.Groups["Name"].Captures.Cast<Capture>().Select((v, i) => new { value = v.Value, index = i }), a => a.index, b => b.index, (a, b) => new { Code = a.value, Name = b.value, a.index }).Join(match.Groups["Type"].Captures.Cast<Capture>().Select((v, i) => new { value = v.Value, index = i }), a => a.index, b => b.index, (a, b) => new Fund { Code = a.Code, Name = a.Name, Type = b.value }).Where(w => (findstring is null || (w.Code.Contains(findstring) || w.Name.Contains(findstring) || w.Type.Contains(findstring))) && !w.Name.EndsWith("(后端)"));
      return s;
    }

获取更新基金明细数据

public static void GetFundDetail(Fund fundCode)
    {
      if (fundCode.Detail == null)
      {
        if (fundCode.Type == "货币型" || fundCode.Type == "QDII")
        {
          return;
        }
        string resultstr = Util.HttpCommon.HttpGet($"https://fund.eastmoney.com/pingzhongdata/{fundCode.Code}.js?v={DateTime.Now.ToString("yyyyMMddHHmmss")}");

        if (resultstr.StartsWith("远程服务器返回错误"))
        {
          return;
        }

        Regex regex = new Regex("var Data_netWorthTrend = (?<Data>\\[(\\{\"x\"\\:(?<Date>[0-9]{13})\\,\"y\"\\:(?<Value>.*?)\\,.*?\\}[\\,]{0,1})+\\])");
        Match match = regex.Match(resultstr);
        fundCode.StartRecordTimeSpan = match.Groups["Date"].Captures.Cast<Capture>().First().Value;
        fundCode.LastRecordTimeSpan = match.Groups["Date"].Captures.Cast<Capture>().Last().Value;
        //fundCode.Detail = 
        fundCode.Detail = match.Groups["Date"].Captures.Cast<Capture>().Select((v, i) => new { index = i, value = Convert.ToSingle(v.Value) / 100000 }).Join(match.Groups["Value"].Captures.Cast<Capture>().Select((v, i) => new { index = i, value = Convert.ToDouble(v.Value) }), a => a.index, b => b.index, (a, b) => new Point { X = a.value, Y = b.value });
      }
      //DataService.UpdateFundDetail(fundCode);
    }

也就是只要想从网上新的一种数据,就需要重新创建一个方法

但是访问网络数据具有共同的逻辑

0.判断输入的合法性,调用网络接口的前提条件

1.根据输入的url发送一个HTTP Get请求,接收string结果

2.处理返回异常

3.通过正则解析结果,

4.将结果拼接成List列表返回

通过泛型可以只声明一个方法,向调用方暴露必要的输入信息,以及需要的返回类型数据

泛型实现

 /// <summary>
  /// 可数据服务的,使用泛型获取数据
  /// </summary>
  public static class DataServiceable
  {
    public static IEnumerable<T> GetData<T>(this DataService S, string url, string regexString, Func<T, string, string, bool> CallBackSetting) where T : new()
    {
      Regex regex = new Regex("\\(\\?\\<(?<Key>.*?)\\>");

      MatchCollection matchs = regex.Matches(regexString);

      List<string> keys = matchs.Cast<Match>().Select(s => s.Groups["Key"].Value).ToList();

      regex = new Regex(regexString);
      string resultstr = Util.HttpCommon.HttpGet(url);

      if (resultstr.StartsWith("远程服务器返回错误"))
      {
        return null;
      }

      Match match = regex.Match(resultstr);

      List<T> a = new List<T>();
      T t;
      foreach (string item in keys)
      {
        for (int i = 0; i < match.Groups[item].Captures.Count; i++)
        {
          if (a.Count > i)
          {
            t = a[i];
            CallBackSetting(t, item, match.Groups[item].Captures[i].Value);
          }
          else
          {
            t = new T();
            if (CallBackSetting(t, item, match.Groups[item].Captures[i].Value))
            {
              a.Add(t);
            }
          }

        }

      }
      return a;
    }
  }

定义了一个扩展方法,向DataService的实例添加一个GetData方法,其中GetData<T>让用户确定T是什么类型,系统返回List<T>

public static IEnumerable<T> GetData<T>(this DataService S, string url, 
string regexString, 
Func<T, string, string, bool> CallBackSetting) where T : new()
{...}

系统向调用方暴露需要输入的web地址,符合格式的正则字符串,以及如何将正则字符串中的标签和输出表字段关联起来的赋值逻辑,最后一个where T : new() 表示泛型必须要有一个无参的构造函数,这时系统就可以在内部直接创建一个泛型T的实例

t = new T();

调用委托调用方实现的方法

CallBackSetting(t, item, match.Groups[item].Captures[i].Value)

让调用方对泛型T数实例赋值

最终将赋值好的t添加到泛型List<T>的实例

List<T> a = new List<T>();
T t;

。。。
t = new T();
if (CallBackSetting(t, item, match.Groups[item].Captures[i].Value))
{
      a.Add(t);
}
。。。

return a;

调用泛型方法

DataService dataService = new DataService();
List<Fund> funds =dataService.GetData<Fund>(
        @"http://fund.eastmoney.com/js/fundcode_search.js?v=" + DateTime.Now.ToString("yyyyMMddHHmmss"), "var r = (?<Data>\\[(\\[\"(?<Code>[0-9]{6})\"\\,\".*?\"\\,\"(?<Name>.*?)\"\\,\"(?<Type>.*?)\".*?\\][\\,]{0,1})+\\])",
        (a, capture, u) => {
          switch (capture)
          {
            case "Code":
              a.Code = u;
              break;
            case "Type":
              a.Type = u;
              break;
            case "Name":
              a.Name = u;
              break;
            default:
              return false;
          }
          return true;
        }).Where(w => (findstring is null || (w.Code.Contains(findstring) || w.Name.Contains(findstring) || w.Type.Contains(findstring))) && !w.Name.EndsWith("(后端)"));

直接返回一个LIst<Fund>数据集

如果要以相同的方式获取其他的网络数据

List<Point> points = dataService.GetData<Point>(
        $"https://fund.eastmoney.com/pingzhongdata/{fundCode.Code}.js?v={DateTime.Now.ToString("yyyyMMddHHmmss")}", "var Data_netWorthTrend = (?<Data>\\[(\\{\"x\"\\:(?<Date>[0-9]{13})\\,\"y\"\\:(?<Value>.*?)\\,.*?\\}[\\,]{0,1})+\\])",
        (a, capture, u) =>
        {
          switch (capture)
          {
            case "Date":
              a.X = Convert.ToSingle(u) / 100000;
              break;
            case "Value":
              a.Y = Convert.ToDouble(u);
              break;
            default:
              return false;
          }
          return true;
        });

委托方法通过Lameda表达式实现,系统将正则匹配的结果和正则标签作为传入参数,让调用方如何确定将该标签的值传给实例元素的哪个字端,调用方返回一个bool类型数据,告诉系统是否有赋值操作,系统根据这个返回值确定是否需要将让调用方更新后的实例添加到List列表中。

系统直接返回一个点集List<Point>

调用方完全不需要关心系统是如何将通过地址去访问网络数据,如何处理异常,如何处理正则匹配,如何构造List和添加元素,只需要调用方将需要的信息声明出来,系统就知道如何处理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值