2016年6月23日 星期四

wkHTMLToPDF及TuesPechkin的使用

因專案需求需要運用到HTML轉PDF,之前有使用過iTextSharp轉過PDF檔,但這次HTML有包含圖片檔導致轉出來有些不正常,後來找到[C#] 網頁Html轉PDF檔(一行程式碼解決) ,發現有這麼方便的工具,而且轉出來也很正常,這是在主機上安裝一個EXE的執行程式,可以到wkhtmltopdf官網下載並安裝,要注意的是除了有分32/64還有分作業系統,Windows就有分2003/XP及7/vista,看起來是編譯的方式不一樣,程式代碼如下
public ActionResult Print()
{
    Process process = new Process();
    string serverPath = Server.MapPath("~");
    string filePath = serverPath + @"\Temp\" + "test.pdf";
    //
    process.StartInfo.UseShellExecute = true;
    //wkhtmltopdf.exe應用程式放的位置
    process.StartInfo.FileName = serverPath + @"\wkhtmltopdf.exe";
    //參數為網址 + " " + 實體檔案位置
    process.StartInfo.Arguments = "http://www.google.com.tw" + " " + filePath;
    process.Start();
    //等待執行完成
    process.WaitForExit();
    //
    return File(filePath, "application/pdf", "工作報告單");
}
後來因為一些問題有看到黑暗大的一篇文章HTML轉PDF - 使用Pechkin套件,發現有人把wkhtmltopdf轉成DLL可用的套件,但同時又看到[ASP.net MVC] 在Web專案上使用Pechkin套件將網頁轉成PDF檔裡面說到此套件的DLL有BUG,原作者有另外更新此DLL。
但我在NuGet上找這個套件時又看到TuesPeckin的套件,查看了一下他骨子裡也是同一套,就想說用看看,果然產出的一模一樣。 相關做法如下:
  1. 先到NuGet上下安裝TuesPechkin,另外看是在32/64位元環境,分別安裝TuesPechkin.Wkhtmltox.Win32/TuesPechkin.Wkhtmltox.Win64
  2. 引用程式代碼
  3. using TuesPechkin;
    
    public class HtmlToPDF
    {
        public IConverter Converter { get; private set; }
        public IConverter anotherConverter { get; private set; }
        public string Url { get; private set; }
    
        public HtmlToPDF(string url)
        {
            Url = url;
        }
    
        public byte[] GetPDF()
        {
            var doc = new HtmlToPdfDocument();
            var objSettings = new ObjectSettings()
            {
                PageUrl = Url
            };
            doc.Objects.Add(objSettings);
            Converter =
                new ThreadSafeConverter(
                    new RemotingToolset(
                        new Win32EmbeddedDeployment(
                            new TempFolderDeployment())));
            var result = Converter.Convert(doc);
            //
            return result;
        }
    }
    
    public ActionResult Print()
    {
        HtmlToPDF htmlToPdf = new HtmlToPDF("http://www.google.com.tw");
        byte[] pdfFile = htmlToPdf.GetPDF();
        //
        return File(pdfFile, "application/pdf", "test.pdf");
    }
    
參考網址:
[C#] 網頁Html轉PDF檔(一行程式碼解決)
wkhtmltopdf官網
HTML轉PDF - 使用Pechkin套件
[ASP.net MVC] 在Web專案上使用Pechkin套件將網頁轉成PDF檔
TuesPeckin

2016年6月12日 星期日

深入理解C#(C# in Depth) 讀書心得 Part 1

Part 1 基礎知識(Preparing for the journey)
  1. C#開發的進化史(The changing face of C# development)
    •  C#從1.0到4.0,透過一個簡單的例子說明各版本的寫法優劣差異,及各版本的一些特性。
  2. C#1所搭建的核心基礎(Core foundations:building on C# 1)
    • 委派(Deletates)
      •  委派的4個條件
        1. 宣告委派類型
        2.     //宣告委派類型
              delegate void StringProcessor(string input);
          
        3. 必需有一個執行代碼的方法,方法的參數及回傳的類型皆要一致
        4.     public class Person
              {
                  string name;
                  public Person(string name)
                  {
                      this.name = name;
                  }
          
                  public void Say(string message)
                  {
                      Console.WriteLine("{0} says: {1}", name, message);
                  }
              }
          
        5. 必須建立一個委派實例化,可以是靜態方法或參考方法
        6.     Person jon = new Person("Jon");
              Person tom = new Person("Tom");
              StringProcessor jonsVoice, background;
              jonsVoice = new StringProcessor(jon.Say);
          
        7. 必須調用(invoking)委派方法
        8.     jonsVoice += tom.Say;
              jonsVoice.Invoke(Hello, son.);
              //OR
              jonsVoice("Hello, son.");
          
      • 委派可以增加或刪除一個委派實例
      • 事件不是委派實例,只是成對的add/remove方法
    • 系統類型的特色(Type system characteristics) 
      •  C#1~3是靜態類型系統,他是明確的、安全的
      •  靜態與動態類型的差異性在於靜態類型在撰寫上有定義明確的型別,動態類型在撰寫上沒有明確定義型別
      • //靜態類型
        string s = "hello";
        //動態類型
        var s = "hello";
        
      • 動態類型較為不安全,因動態類型必須在執行階段才可知道型別是否正確會不會造成錯誤
      • 靜態類型不允許一個集合成為強型別的string或int,必須要透過強制轉型,這樣就無法達到編譯安全性
      • string[] strings = new string[5];
        object[] objects = strings;
        //這邊會報出ArrayTypeMismatchException
        objects[0] = new Button();
        
      • 可變性分為兩種,共變數(covariance)、反變數(contravariance),共變數就是可將衍生型別轉型成基礎型別(object),反變數則相反,可將基礎型別轉型成衍生型別
      • 方法覆蓋及介面實作皆不支援共變數及反變數
    • 值/參考型別(Value/reference types)
      • 值與參考型別使用分類不能單就效能來取決,要看用在什麼地方,依據情境來決定該使用哪種
      • 參考型別一定在記憶體的堆積(heap)上,但值型別並不一定在記憶體的堆疊(stack)上
      • 物件在C#中預設不是透過參考傳遞(pass by reference)的,這關係到左值(l-value)
      • boxing和unboxing使用上要注意效能問題,盡可能少用
    • 基於前三種基礎的新特性
      • 委派的新特性
        • C# 2.0提供了泛型委派、委派的表達式、匿名的寫法(不直覺)、委派的共變數及反變數
        • //C# 2.0依前面的例子,可用此種方式指派(隱含轉換)
          jonsVoice = jon.Say;
          
        • C# 3.0提供了Lambda的寫法
      • 系統類型的新特性
        • C# 2.0提供泛型
        • C# 3.0提供匿名型別、隱含型別、擴充方法
        • C# 4.0提供動態型別、有限制的泛型共變數及反變數
      • 值/參考型別的新特性
        • C# 2.0提供泛型、可以為null
Part 2 C# 2 解決C# 1 的問題(C# 2 : Solving the issues of C# 1)
  1. 用泛型實現參數化類型(Parameterized typing with generics)
    • 泛型帶來了更好的編譯時檢查、更清楚的看到所要表達的訊息,當然還有IDE的支持及型能的提升,減少boxing和unboxing
    • 泛型有泛型類型(generic types)跟泛型方法(generic methods)
    • 最簡單的泛型就是Dictionary<TKey, TValue>
    • 泛型類型分為兩種,未繫結泛型類型(unbound generic type)就是泛型角框號裡的型別是T的,已建構類型(constructed type)
    • //未繫結泛型類型
      Dictionary<Tkey, Tvalue> unbound = New Dictionary<Tkey, Tvalue>();
      //已建構類型
      Dictionary<string, int> cons = New Dictionary<string, int>();
      
    • 已建構類型有分開放類型(open type)、封閉類型(closed type),未繫結泛型類型基本上就是開放類型,其他皆為封閉類型
    • 泛型方法
    • static double TakeSquareRoot(int x)
      {
          return Math.Sqrt(x);
      }
      List<int> integers = new List<int>();
      integers.Add(1);
      integers.Add(2);
      integers.Add(3);
      integers.Add(4);
      Converter<int double> converter = TakeSquareRoot;
      List<double> doubles;
      //調用泛型方法
      doubles = integers.ConvertAll(converter);
      foreach (double item in doubles)
      { }
      
    • 泛型類型的條件約束(type constraint)
    • //參考型別限制
      struct RefSample<T> where T : class { }
      //值型別限制
      class ValSample<T> where T : struct { }
      //建構式限制
      public T CreateInstance<T>() where T : new()
      {
          return new T();
      } 
      //轉換型別限制
      class Sample<T> where T : Stream
      struct Sample<T> where T : IDisposable
      class Sample<T> where T : IComparable<T>
      //T繼承至U
      class Sample<T, U> where T : U
      //組合限制
      class Sample<T> where T : class, IDisposable, new()
      class Sample<T, U> where T : class where U : struct, T
      
    • 泛型類型的推斷(type inference)
      • 泛型類型推斷只適用於泛型方法
      • static Lits<T, U> MakeList<T, U>(T first, Tsecond);
        List<string, U> list = MakeList<string, U>("Line 1", "Line 2");
        //類型推斷可簡化寫法成
        List<string, U> list = MakeList("Line 1", "Line 2");
        
    • 實作泛型(Implementing generics),自行撰寫泛型時有幾件事需要注意
      1. 預設關鍵字(Default value Expressions),利用default取出型別的預設值
      2. static int CompareToDefault<T, U>(T value)
            where T : IComparable<T, U>
        {
            return value.CompareTo(default(T));
        }
        Console.WriteLine(CompareToDefault("x")); //1
        Console.WriteLine(CompareToDefault(10)); //1
        Console.WriteLine(CompareToDefault(0)); //0
        Console.WriteLine(CompareToDefault(-10)); //-1
        Console.WriteLine(CompareToDefault(DateTime.MinValue)); //0
        
      3. 直接比較(Direct comparisons),運用到多載運算子(overload operator),泛型T不可直接==或!=比較,必須要運用EqualityComparer<T>、Comparer<T>
      4. static bool AreReferencesEqual<T>(T first, T second)
            where T : class
        {
            return first == second;
        }
        string name = "Jon";
        string intro1 = "My name is " + name;
        string intro2 = "My name is " + name;
        Console.WriteLine(intro1 == intro2);  //true
        Console.WriteLine(AreReferencesEqual(intro1, intro2)); //false
        
      5. 一個完整的泛型實作範例
      6. public sealed class Pair<T1, T2> : IEquatable<Pair<T1, T2>>
            {
                private static readonly IEqualityComparer<T1> FirstComparer =
                    EqualityComparer<T1>.Default;
                private static readonly IEqualityComparer<T2> SecondComparer =
                    EqualityComparer<T2>.Default;
                private readonly T1 first;
                private readonly T2 second;
        
                public Pair(T1 first, T2 second)
                {
                    this.first = first;
                    this.second = second;
                }
        
                public T1 First { get { return first; } }
                public T2 Second { get { return second; } }
        
                public bool Equals(Pair<T1, T2> other)
                {
                    return other != null &&
                        FirstComparer.Equals(this.First, other.First) &&
                        SecondComparer.Equals(this.Second, other.Second);
                }
        
                public override bool Equals(object o)
                {
                    return Equals(o as Pair<T1, T2>);
                }
        
                public override int GetHashCode()
                {
                    return FirstComparer.GetHashCode(first) * 37 +
                    SecondComparer.GetHashCode(second);
                }
            }
        
    • 高級泛型(Advanced generics)
      • 回頭再看一次
    • 泛型在C#的限制(Limitations of generics in C#)
      • 缺乏可變性,這部分我倒認為蠻好的,這確保了型別的安全性,在C#4.0前有一些方式可處理共變數及反變數,但處理的方式我覺得都不太直覺
      • 缺乏運算子操作約束,也就是在泛型類別裡,運用到 += 這種運算子去加T型別時造成的問題,C#4.0前沒有好的方法可解決
      • 缺乏泛型屬性、索引器和其他成員類型,這個問題一般來說不太會遇到
  2. Null的類型 (Saying nothinh with nullable types)
    • 在C# 1.0 時還沒有nullable的型別,有三種模式可以來表示nullable 
      • 魔術值(The magic value),基本上就是定義一個空值讓耀表示空值得來指定,像Datetime可以把空值指定到Datetime.MinValue,ADO.NET就會使用DBNULL.Value來表示
      • 包裝參考型別
      • 額外的註記
    •  System.Nullable<T> and System.Nullable
      • System.Nullable<T>是Value Type
      • 有HasValue及Value,HasValue提供Boolen
      • GetValueOrDefault如果沒有值則基於基底型別提供預設值,有則同Value,或者可以給予預設值,ex : GetValueOrDefault(10)
    • C# 2 的語法糖
      • 使用?來寫跟System.Nullable<T>一樣,ex: int?
      • 提到Nullable與非Nullable的比較及運算式,這比較沒什麼爭議,基本上很直覺的可以判斷
      •  可以使用as語法來判斷是否可轉型,確保型別判斷正確,但這部分有效能考量,這牽扯到boxing和unboxing,使用上得注意
      • static void PrintValueAsInt32(object o)
        {
            int? nullable = o as int?;
            Console.WriteLine(nullable.HasValue ?
                              nullable.Value.ToString() : "null");
        }
        PrintValueAsInt32(5); //5
        PrintValueAsInt32("some string"); //null
        
      • 使用 ?? 的語法(null coalescing operator)來簡化程式碼並提高可讀性,原則上第一個值的判斷必須要是可為Null的型別,不然就毫無意義了
      • //原本的寫法
        DateTime lastAlive = (death == null ? DateTime.Now : death.Value);
        return lastAlive - birth;
        //??的方式
        DateTime lastAlive = death ?? DateTime.Now;
        return lastAlive - birth;
        //也可簡化成1行
        return (death ?? DateTime.Now) - birth;
        //判斷的變數的數量沒有限制,可寫成這樣
        Address contact = user.ContactAddress ??
                          order.ShippingAddress ??
                          user.BillingAddress;
        
    • Nullable的額外特殊用法,利用??的語法延伸出的變化,下面範例是可以簡化類別比較的寫法
    • public static class PartialComparer
      {
         public static int? Compare<T>(T first, T second)
         {
            return Compare(Comparer<T>.Default, first, second);
         }
         public static int? Compare<T>(IComparer<T> comparer,
                                       T first, T second)
         {
            int ret = comparer.Compare(first, second);
            return ret == 0 ? new int?() : ret;
         }
         public static int? ReferenceCompare<T>(T first, T second)
            where T : class
         {
             return first == second ? 0
                  : first == null ? -1
                  : second == null ? 1
                  : new int?();
         } 
      }
      public int Compare(Product first, Product second)
      {
          return PC.ReferenceCompare(first, second) ??
                 PC.Compare(second.Popularity, first.Popularity) ??
                 PC.Compare(first.Price, second.Price) ??
                 PC.Compare(first.Name, second.Name) ??
                 0;
      }
      
  3. 進入快速通道的委派(Fast-tracked delegates)
    • 跟笨拙的委派語法說拜拜(Saying goodbye to awkward delegate syntax),以下範例是C# 1.0
    • static void LogPlainEvent(object sender, EventArgs e)
      {
         Console.WriteLine("LogPlain");
      }
      static void LogKeyEvent(object sender, KeyPressEventArgs e)
      {
         Console.WriteLine("LogKey");
      }
      static void LogMouseEvent(object sender, MouseEventArgs e)
      {
         Console.WriteLine("LogMouse");
      }
      ...
      Button button = new Button();
      button.Text = "Click me";
      button.Click += new EventHandler(LogPlainEvent); 
      button.KeyPress += new KeyPressEventHandler(LogKeyEvent); 
      button.MouseClick += new MouseEventHandler(LogMouseEvent);
      Form form = new Form();
      form.AutoSize = true;
      form.Controls.Add(button);
      Application.Run(form);
      
    • 群組方法轉換(Method group conversions),可簡略語法,但還不算是很好的寫法
    • 共變數與反變數(Covariance and contravariance),下面範例是前面看到的範例改以C# 2.0的方式,看起來沒什麼意義只是要表達這樣的寫法
    • static void LogPlainEvent(object sender, EventArgs e)
      {
         Console.WriteLine("An event occurred");
      }
      Button button = new Button();
      button.Text = "Click me";
      button.Click += LogPlainEvent;
      button.KeyPress += LogPlainEvent;
      button.MouseClick += LogPlainEvent;
      Form form = new Form();
      form.AutoSize = true;
      form.Controls.Add(button);
      Application.Run(form);
      
    • 委派回傳值的反變數(Covariance of delegate return types),也就是是否為上層型別可否轉型,以下範例表示MemoryStream可轉型成Stream,但反過來就不行
    • delegate Stream StreamFactory();
      static MemoryStream GenerateSampleData()
      {
         byte[] buffer = new byte[16];
         for (int i = 0; i < buffer.Length; i++)
         {
            buffer[i] = (byte) i;
         }
         return new MemoryStream(buffer);
      }
      StreamFactory factory = GenerateSampleData;
      using (Stream stream = factory())
      {
         int data;
         while ((data = stream.ReadByte()) != -1)
         {
            Console.WriteLine(data);
         }
      }
      
    • 使用匿名方法的委派實作(Inline delegate actions with anonymous methods) 
      • 處理一個參數(action on  a parameter),運用泛型委派Action<T>
      • Action<string> printReverse = delegate(string text)
        {
           char[] chars = text.ToCharArray();
           Array.Reverse(chars);
           Console.WriteLine(new string(chars));
        };
        Action<int> printRoot = delegate(int number)
        {
           Console.WriteLine(Math.Sqrt(number));
        };
        Action<IList<double>> printMean = delegate(IList<double> numbers) {
           double total = 0;
           foreach (double value in numbers)
           {
              total += value;
           Console.WriteLine(total / numbers.Count);
        };
        printReverse("Hello world");
        printRoot(2);
        printMean(new double[] { 1.5, 2.5, 3, 4.5 });
        
      • 前面範例的匿名方法有點繁瑣,我們在用更精簡的方式
      • List<int> x = new List<int>();
        x.Add(5);
        x.Add(10);
        x.Add(15);
        x.Add(20);
        x.Add(25);
        x.ForEach(delegate(int n) { 
            Console.WriteLine(Math.Sqrt(n));
        });
        
      • 匿名方法回傳值,第一個範例我們用C#2內建的Predicate來實作判斷值回傳true/false,第二個範例我們一樣用C#2的Comparison來實作排序的比較
      • Predicate isEven = delegate(int x) { return x % 2 == 0; };
        Console.WriteLine(isEven(1));
        Console.WriteLine(isEven(4));
        
        static void SortAndShowFiles(string title, Comparison sortOrder)
        {
            string[] fileName = Directory.GetFiles(@"C:\");
            FileInfo[] files = new FileInfo[fileName.Length];
            //
            for (int i0 = 0; i0 < fileName.Length; i0++)
            {
                files[i0] = new FileInfo(fileName[i0]);
            }
            Array.Sort(files, sortOrder);
            //
            Console.WriteLine(title);
            foreach (FileInfo file in files)
            {
                Console.WriteLine(" {0} ({1} bytes)", file.Name, file.Length);
            }
        }
        SortAndShowFiles("Sorted by name:", delegate (FileInfo f1, FileInfo f2) {
            return f1.Name.CompareTo(f2.Name);
        });
        SortAndShowFiles("Sorted by length:", delegate (FileInfo f1, FileInfo f2) {
            return f1.Length.CompareTo(f2.Length);
        });
        
      •  忽略委派的參數,利用前面Button的範例再來改寫一下,用匿名方法不帶參數的方式,大大的簡化了程式碼
      • Button button = new Button();
        button.Text = "Click me";
        button.Click += delegate { Console.WriteLine("LogPlain"); };
        button.KeyPress += delegate { Console.WriteLine("LogKey"); };
        button.MouseClick += delegate { Console.WriteLine("LogMouse"); };
        Form form = new Form();
        form.AutoSize = true;
        form.Controls.Add(button);
        Application.Run(form);
        
    • 在匿名方法裡抓取變數(Capturing variables in anonymous methods)
      • 這邊所代表意思就是在定義匿名方法時有用到外層的變數,因為執行的階段不一樣所以會導致變數可能在某個時間點被變更,匿名方法要等到被執行時才抓取當下的變數值,如以下範例
      • string captured = "before x is created";
        MethodInvoker x = delegate
        {
            Console.WriteLine(captured);
            captured = "changed by x";
        };
        captured = "directly before x is invoked";
        x();
        Console.WriteLine(captured);
        captured = "before second invocation";
        x();
        //執行這段城市會得到以下的結果
        directly before x is invoked
        changed by x
        before second invocation
        
      • 再來看點複雜的範例,如果有變數是在內部宣告及外部宣告時的情境,因為在for迴圈裡宣告變數時會視為各迴圈的獨立變數個體,所以傳進委派方法時都會是獨立的,下面範例foreach會輸出0,10,20,30,40,再來分別是1,2,3 11
      • List<methodinvoker> list = new List<methodinvoker>();
        for (int index = 0; index < 5; index++)
        {
            int counter = index * 10;
            list.Add(delegate
            {
                Console.WriteLine(counter);
                counter++;
            });
        }
        foreach (MethodInvoker t in list)
        {
            t();
        }
        list[0]();
        list[0]();
        list[0]();
        //
        list[1]();
        
  4. 簡單實作迭代器(Implementing iterators the easy way)
    • C# 1.0必須自己刻出iterators的實作,要自行記錄狀態,如MoveNext、Current、Reset等,非常之麻煩。
    • C# 2.0提供yield的方式來達到iterators的實作,大大的減少瑣碎的程式碼撰寫
      • 來看個範例,這個範例主要是顯示出iterators的執行過程 
        static readonly string Padding = new string(' ', 30);
        static IEnumerable<int> CreateEnumerable()
        {
            Console.WriteLine("{0}Start of CreateEnumerable()", Padding);
            for (int i = 0; i < 3; i++)
            {
                Console.WriteLine("{0}About to yield {1}", Padding, i);
                yield return i;
                Console.WriteLine("{0}After yield", Padding);
            }
            Console.WriteLine("{0}Yielding final value", Padding);
            yield return -1;
            Console.WriteLine("{0}End of CreateEnumerable()", Padding);
        }
        IEnumerable<int> iterable = CreateEnumerable();
        IEnumerator<int> iterator = iterable.GetEnumerator();
        Console.WriteLine("Starting to iterate");
        while (true)
        {
            Console.WriteLine("Calling MoveNext()...");
            bool result = iterator.MoveNext();
            Console.WriteLine("... MoveNext result={0}", result);
            if (!result)
            {
                break;
            }
            Console.WriteLine("Fetching Current...");
            Console.WriteLine("... Current result={0}", iterator.Current);
        }
        
        有幾點要注意幾個地方
        1. CreateEnumerable執行直到MoveNext才開始執行方法裡的程式
        2. 執行Current不會執行任何程式
        3. yeild return執行完就停住直到下次MoveNext
        4. 一個方法可以有多個yeild return
        5. 方法會直到MoveNext為false才會結束
      • 它可以在執行過程中中斷它,yeild break,另外還可以在裡面下try..Catch..Finally,如果不是用yeild break而是直接return做停止執行iterators時finally理論上不會執行,但他會等到最後離開foreach時執行,這是比較特別的地方
      • 這邊有幾點要了解的
        1. 在第一次執行MoveNext之前,取得Current會返回型別的預設值
        2. MoveNext返回false後,Current會儲存最後一個值
        3. Reset是不實作的
    • 真實使用iterators,這邊作者列了3個例子
      • 時刻表中的日期
      • static IEnumerable<DateTime> DateRange
        {
            get
            {
                for (DateTime day = new DateTime(2016,6,3); day <= new DateTime(2016, 07, 01); day = day.AddDays(1))
                {
                    yield return day;
                }
            }
        }
        foreach (var item in DateRange)
        {
            //列出日期
            var a = item;
        }
        
      • 列出文件
      • foreach (var item in ReadLines("C:/Programing/test.txt"))
        {
            string a = item;
        }
        static IEnumerable<string> ReadLines(Func<TextReader> provider)
        {
            using (TextReader reader = provider())
            {
                string line;
                while ((line = reader.ReadLine()) != null)
                {
                    yield return line;
                }
            }
        }
        static IEnumerable<string> ReadLines(string filename)
        {
            return ReadLines(delegate {
                return File.OpenText(filename);
            });
        }
        
      • 利用謂詞作篩選
      • IEnumerable<string> lines = ReadLines("C:/Programing/test.txt");
        Predicate<string> predicate = delegate (string line)
            { return line.StartsWith("abc"); };
        foreach (string line in Where(lines, predicate))
        {
            Console.WriteLine(line);
        }
        public static IEnumerable<T> Where<T>(IEnumerable<T> source,
            Predicate<T> predicate)
        {
            if (source == null || predicate == null)
            {
                throw new ArgumentNullException();
            }
            return WhereImpl(source, predicate);
        }
        private static IEnumerable<T> WhereImpl<T>(IEnumerable<T> source,
            Predicate<T> predicate)
        {
            foreach (T item in source)
            {
                 if (predicate(item))
                 {
                     yield return item;
                 }
            }
        }
        
  5. 剩下的C# 2.0的新特性(Conculding C# 2 the final features)
    • partial types部分類型,就是class及method可以建立成部分,可分布在不同的檔案,通常是用在有程式碼產生器產出的類別中可以讓程式設計師惡外加東西在類別上,這是就可另外建立partial避免程式碼被洗掉,或是有需要分工的部分也可以用partial做區分;在C# 3.0有多增加一個特性,就是假設在partial裡只有宣告但沒指定method的實作時編譯器會忽略此段code,避免程式碼出錯,如以下範例
    • // Generated.cs
      using System;
      partial class PartialMethodDemo
      {
          public PartialMethodDemo()
          {
              OnConstructorStart();  //這段在編譯器就看不到
              Console.WriteLine("Generated constructor");
              OnConstructorEnd();
          }
          partial void OnConstructorStart();
          partial void OnConstructorEnd();
      }
      // Handwritten.cs
      using System;
      partial class PartialMethodDemo
      {
          partial void OnConstructorEnd()
          {
              Console.WriteLine("Manual code");
          }
      }
      
    • static class靜態類別,在C# 1.0時只有靜態方法,靜態類別有幾點要注意
      • 不可宣告為抽象類別(abstract)及密封類別(sealed)
      • 不可繼承介面並實作類別 
      • 不可有任何非靜態的方法,包含建構子
      • 不可以有運算子
      • 不可有protected或protected internal
    • 獨立的getter/setter 屬性,C# 1.0不能定義public get / private set
    • 命名空間別名,如果有相同的名字在不同的命名空間時可使用
      • C# 2.0 增加 :: 別名的使用
      • using System;
        using WinForms = System.Windows.Forms;
        using WebForms = System.Web.UI.WebControls;
        class WinForms {}
        class Test {
            static void Main()
            {
                Console.WriteLine(typeof(WinForms::Button));
                Console.WriteLine(typeof(WebForms::Button)); 
            }
        }
        
      • 使用global::,例如有兩個class,一個有命名空間另一個沒有的情況下可使用
      • using System;
        class Configuration {}
        namespace Chapter7
        {
            class Configuration {}
            class Test {
                static void Main()
                {
                    Console.WriteLine(typeof(Configuration)); 
                    Console.WriteLine(typeof(global::Configuration)); 
                    Console.WriteLine(typeof(global::Chapter7.Test));
                }
            } 
        }
        
      • 外部別名(Extern allases),如引用兩個外部類別庫都有相同的命名空間及方法時可使用
      • // Compile with
        // csc Test.cs /r:FirstAlias=First.dll /r:SecondAlias=Second.dll
        extern alias FirstAlias;
        extern alias SecondAlias;
        using System;
        using FD = FirstAlias::Demo;
        class Test 
        {
            static void Main()
            {
                Console.WriteLine(typeof(FD.Example));
                Console.WriteLine(typeof(SecondAlias::Demo.Example));
            }
        }
        
    • 使用Pragma指令
      • 在VS撰寫程式碼時常常會有一些警告訊息,如變數未使用等,通常都會去修正不正確的地方,Pragma的指令是讓你可以暫時忽略這些警告,比如像以下的範例
      • public class FieldUsedOnlyByReflection {
        #pragma warning disable 0169
            int x;
        #pragma warning restore 0169
        }
        
    • 非安全程式碼中的固定暫存大小(Fixed-size buffers in unsafe code)
      • 可以使用fixed陳述式,在資料結構中建立具有固定大小陣列的緩衝區
      • private fixed char name[30];
        
    • 把內部的成員暴露給指定的類別庫(Exposing Internal members to selected assembles)
      • 利用授權方式讀取
      • // Compiled to Source.dll
        using System.Runtime.CompilerServices; 
        [assembly:InternalsVisibleTo("FriendAssembly")] 
        public class Source
        {
           internal static void InternalMethod() {}
           public static void PublicMethod() {}
        }
        // Compiled to FriendAssembly.dll
        public class Friend
        {
           static void Main()
           {
              Source.InternalMethod();
              Source.PublicMethod();
           }
        }
        // Compiled to EnemyAssembly.dll
        public class Enemy
        {
           static void Main()
           {
              // Source.InternalMethod();
              Source.PublicMethod();
           }
        }
        
      • 通常會使用到他的地方是在寫單元測試的時候,但也不是必要的
    • 這邊提到剩下的C# 2.0新增的特性也不是非常必要或是常用的,但有需要時可以使用.

2016年6月3日 星期五

升級CrystalReport遇到的狀況

昨天升級完.NET從2.0到3.5,想說應該都OK了,結果測一下報表發現有錯誤訊息,錯誤點在於要將ReportSource指到CrystalReportSource.ReportDocument.Load時發生問題,訊息如下"為具有 CLSID {5FF57840-5172-4482-9CA3-541C7878AE0F} 的元件擷取 COM Class Factory 失敗: 800736b1",看到這訊息直覺就是元件沒安裝好,但又覺得那悶,我裝了之前從官網上下載的CrystalReport安裝檔For VS2008的,檔名是CrystalReports2007_x86.msi(印象中之前裝好像可以用的說),後來又用手動把元件重新註冊好像還是一樣,最後想到在安裝VS2008時,安裝程式都會把CrystalReport的安裝檔放在電腦的某個地方,位置在C:\Program Files (x86)\Microsoft SDKs\Windows\v6.0A\Bootstrapper\Packages\CrystalReports10_5,裡面有x86跟x64的版本,想說試看看用這個版本裝到Server上,結果就正常啟動了,呼~~,終於解決了

2016年6月2日 星期四

升級.NET Framwork遇到的怪事

今天為了一些原因把網站從.NET 2.0 升級到.NET 3.5,轉完後再VS上一切都正常,架在公司的測試機Windows 2003 Server上也都正常執行,想說沒甚麼問題要把它佈署到正式環境上發生了一件事,請看圖

在Web.config出現了此訊息"已經定義過區段或群組名稱 'scriptResourceHandler'。您只能在定義它的組態層級中更新它",一開始還以為是正式機上的.NET沒升上3.5或.NET AJAX沒安裝,但經測試後都不是,原因在於正式機的網站上有架一個站台上面是跑.NET 2.0的,我在此網站上建立一個虛擬目錄,Web.config是設定.NET 3.5,原來是這樣導致IIS一直說我重複載入。如果站台沒有指定.NET Framework則各自虛擬目錄底下的版本可以各自獨立。