程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> .NET實例教程 >> :[格式化輸出] 關於IFormattable, IFormatProvider 和 ICustomFormatter的概念和使用

:[格式化輸出] 關於IFormattable, IFormatProvider 和 ICustomFormatter的概念和使用

編輯:.NET實例教程

曾經一度為格式化輸出而困惑,看著滿天遍野的結構,都不敢去輕易觸動。只能使用最安全,但是 低能的object.ToString()方法。終於忍受不了這種窘困的處境,下力氣研究一番,也算是有點心得,希望和大家交流一下。

鑒於該格式化輸出的結構過於繁瑣,我不希望文章陷入條款的解釋,於是,我從一個實際問題入手,一步一步地介紹格式化輸出的概念,三個接口的意義,以及使用接口的一種模式(Pattern)。

1. PhoneNumber 類

假設我們有如下的一個類,用於存儲我們的手機號碼。該類提供了簡單的ToString()實現,即直接返回含有短線(-)的電話號碼。



class PhoneNumber 
...{
   private string _number = "139-0814-2314";

   public PhoneNumber() ...{ }
   public PhoneNumber(string number)
   ...{
      _number = number;
   }
   public string Number
   ...{
      get ...{ return _number; }

 set ...{ _number = value; }
   }

   public override string ToString()
   ...{
      return _number.ToString();
   }
}

2. 提供去掉短線的輸出格式 (IFormattable)

現在,我們希望能輸出不含有短線的電話號碼。一種最簡單的辦法,就是提供一個屬性或者方法來實現。然而,我們希望能夠像以貨幣格式輸出整數一樣,能夠用類似的方法,以不含有短線的方式,輸出電話號碼。即:



//我們通過格式字符串(formatString),可以以貨幣格式輸出整數。
int d = 100;
string val = string.Format("{0:D}",d);
Console.WriteLine(val);
//結果是(結果會因為區域設置有不同,但結構是類似的,即在數字前會出現貨幣符號)
//¥100

//我們希望能夠通過如下的方式,輸出不含短線的電話號碼。
PhoneNumber pn = new PhoneNumber();
string val = string.Format("{0:R}",pn);
Console.WriteLine(val);
//期望的結果是:
//13908142314

我們需要作的,就是實現IFormattable接口:



      class PhoneNumber : IFormattable
      ...{
         //同上
         public override string ToString()
  ...{
            return ToString(null, null);

         }

         IFormattable Members#region IFormattable Members
         public string ToString(string format, IFormatProvider formatProvider)
         ...{
            string _val = string.Empty;

            switch (format)
            ...{
               case "R":
                  _val = _number.Replace("-", "");
                  break;
               default:
                  _val = _number.ToString(formatProvider);
                  break;
            }
            return _val;

         }
         #endregion
      }

現在,一切工作的非常良好,我們的PhoneNumber類也非常順利的發布出去了。一切看起來都非常不錯!

3. 提供隱藏部分號碼的輸出格式 (IFormatProvider, ICustomFormatter)

在使用中,用戶打電話問訊我們,能否提供隱藏部分號碼的輸出格式,即13908142314輸出為139****2314。“這個容易,我們馬上更新”,放下電話,我們在switch中加入了一項,就能解決問題。可是,這樣做,一個潛在的問題是,每次我們得到新的需求,都需要修改我們的PhoneNumber類,這樣做,每次用戶都需要重新編譯PhoneNumber類,以及所有和它相關的類,這樣是一個高風險,違背軟件設計原則的解決辦法。於是,我們應該采用如下的模式來實現擴展的需求:

首先,實現一個單獨的HideFormat類,實現兩個接口。這個類就是我們自己定義的格式規則。



            class HideFormat : IFormatProvider, ICustomFormatter
      ...{
         IFormatProvider Members#region IFormatProvider Members
         public object GetFormat(Type formatType)
         ...{
            if (typeof(ICustomFormatter) == formatType)
            ...{
               return this;
            }
    ;        return null;
         }
         #endregion

         ICustomFormatter Members#region ICustomFormatter Members
         public string Format(string format, object arg, IFormatProvider formatProvider)
         ...{
            // Now the formatProvider must have a ICustomFormatter

            if (arg == null)
            ...{
               throw new ArgumentException("The object should not be null");
            }

            // Describe the formatter rules here
            string _val;
            switch (format)
      ...{
               case "H":
                  _val = arg.ToString().Replace("-", "");
                  _val = _val.Substring(0, 3) + "****" + _val.Substring(7, 4);
                  break;
               default:
                  _val = arg.ToString();
                  break;
            }
            return _val;

         }
         #endregion
      }

然後,修改我們的PhoneNumber類(只需要修改一次)。



      class PhoneNumber : IFormattable
      ...{
         //同上

         IFormattable Members#region IFormattable Members
         public string ToString(string format, IFormatProvider formatProvider)
   ...{
            string _val = string.Empty;

            //如果傳入的是我們自己定義的格式,則使用這個格式去處理我們的格式化請求。
            if (formatProvider != null)
            ...{
               ICustomFormatter formatter = formatProvider.GetFormat(typeof(ICustomFormatter))
                  as ICustomFormatter;
               if (null != formatter)
               ...{
                  return formatter.Format(format, this, formatProvider);
               }
            }
   
   //如果傳入的格式為空,或者是系統默認的格式,則按照系統默認的方式去處理我們的格式化請求。
            switch (format)
            ...{
               case "R":
                  _val = _number.Replace("-", "");
          break;
               default:
                  _val = _number.ToString(formatProvider);
                  break;
            }
            return _val;

         }
         #endregion
      }

然後,為了輸出隱藏部分號碼的格式。我們只需要做如下的調用:



PhoneNumber pn = new PhoneNumber();
SecureFormat sf = new SecureFormat();

string _val;
_val = string.Format(sf,"{0:H}", pn);
Console.WriteLine(_val);  //輸出結果是139****2314
   
_val = pn.ToString("H",sf);
Console.WriteLine(_val); //輸出結果是139****2314

自此,我們完成了所有的任務和更新。最後,讓我們看看擴展性。如果以後我們接到了新的格式申請,我們需要作的,就是實現一個類似SecureFormat的自定義的格式類,然後交給用戶使用。不需要我們修改PhoneNumber的任何實現。這個解決方案,很好的體現了開放-閉合(Open-Close Principle)的軟件設計原則。

作者:Shu Liu(本人系.Net菜鳥,希望大家給予意見和建議)

Mail: [email protected]

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved