2012年6月27日 星期三

[轉貼] C# Delegate/event 的簡單理解

看這篇前可以先看這篇文章瞭解Delegate的語法與使用方式
本篇原文=================
簡單來說就是增加一層間接層來實現晚綁定,從而獲得更為鬆散的耦合

為什麼要有Delegate? 
假設有兩個不同類實現的對象A和B,A和B分別有方法a和b。現在需要a方法以一觸發,便執行對象B的方法b。 不使用delegate,我們可以在設計A類的時候在將B類作為參數傳入a方法,這樣可以實現我們的要求。但是這樣做有明顯的缺陷:   
    1. 我們需要事先知道B類。   
    2. 如果還存在C類、D類等的方法需要一併執行,這樣必須重新修改A類的代碼。 如果有了  Delegate,我們可以事先設計一個函數指針,這樣當對象A執行a方法的時候,就可以根據該指針指向的,符合該函數指針法則的方法進行執行。這樣,我們就不需要在設計A類的時候知道B類,只需要知道B的b方法的參數和返回值就可以了。如果還有C類、D類方法需要一併執行,也可以添加到該指針指向的數據結構中。


示例代碼分析:
代碼
    class Program
{
static void Main(string[] args)
{
A a
= new A();
B b
= new B(a);
a.Method_A();
}
}


class A
{
public delegate void Delegate_A();
public event Delegate_A da;
public void Method_A()
{
da();
}
}

class B
{
public B(A a)
{
a.da
+= new A.Delegate_A(Method_B);
}
public void Method_B()
{
Console.WriteLine(
"B's method!");
}
}
1. 先看Class A中   
public delegate void Delegate_A(); 這個語句規定了函數指針指向的函數應該符合的規範:沒有返回值,沒有參數傳入。
2. 接著實例化了一個 Delegate_A的對象da,這裡使用event關鍵字表明它是一個事件。 
3. 再看Class B的的構造函數,可見B構造時,往對象a的事件中傳入了Method_B的函數地址(a.da += new A.Delegate_A(Method_B)
4. 在Main函數中執行A對象的Method_A時,觸發了事件da,而da指向了B的方法。這樣即便A對象對B對象一無所知,也同樣能執行B的方法Method_B。這就是Delegate的好處。

Event有什麼用:
實際上在上例中將Event關鍵字刪除程序同樣能執行。之所以要加上Event,是因為在程序編譯時,加上Event以後該Delegate的對象會成為該類的私有成員,這樣外界只能通過 += 和 -=來對Delegate的對象da進行操作,而不能直接進行賦值。

2012年6月26日 星期二

C# 物件配置與記憶體議題

對於實值型別(等同Java中稱的基本型別)一樣是放在stack
在method中也同樣是以複製的形式傳遞
也就是method中作的變動不會影響到原本的實值型別物件
不過若要改變傳遞的形式則可以使用 ref、out 兩個關鍵字

ref的用法為宣告使用call by reference
如:public void swap(ref int x, ref int y){ ... //code for swap x&y }


使用method時亦用ref,如:

int a = 1;    int b = 2;
swap(ref a, ref b);


如此一來a與b的值就會在method呼叫過後改變了
C#在配置物件(參考型別資料)時亦隱含了ref的宣告
所以省略不寫也沒關係
但若明白的了ref宣告
則必須在參數、引述的地方都宣告,否則會引起編譯錯誤,如:

public void dosth(ref int[,] intary){ .... }

int[,] a = new int[]{{1,2},{3,4}}
dosth(ref a);     //如果參數、引數只有其中一方宣告ref會引起錯誤


out關鍵字的作用則是傳出值,如:
public void assignValue(out int x, out int y){
    x = 6;  y = 10;                //必須在method中宣告值
.........
}

int a; int b;                           //未賦予初始值
assignValue(out a, out b);    //此時a = 6, b = 10


不過用法雖然多樣化了,但似乎連Visual Studio Team也不鼓勵ref、out的使用
畢竟若設計師不熟悉用法則容易出錯
一般是建議找替代方法來達成目標

struct的使用可以參考MSDN上的說明-使用結構
使用上有無法繼承與多型的限制,但在僅用來儲存field時會有較class更好的效能
其記憶體配置也是位在stack
所以指派的時候也是使用複製,等同於創造出新的struct
注意這點以避免疏失
而struct作為參數傳遞時,可考慮使用ref,以優化性能(當然這時候要注意值的變化)

C#的class有時候並不提供擴展,如第三方的類別等
要封裝類別只要在class宣告前加入sealed關鍵字,如:sealed class ap{...}
如果打算擴展這些類別,C#提供如下作法:

static class secondAp{
    public static string getAns(this ap apobj, double fi1){...}
}


擴張方法必須是存在於靜態類別中的靜態方法
this ap 指定擴充ap類別

[轉載]強制使用javascript-檢查該頁面是否啟用javascript

原文====================

現在ajax的技術很流行,也用在很多web 2.0的網站上,但該技術是基於使用者端瀏覽器的javascript,萬一使用者把它關閉怎麼辦?還要為它們另外撰寫ajax或javascript特效的替代程式嗎?我不負責任的回答是:不要!

大部份網頁瀏覽器,預設都是啟用javascript,那誰會停用?
第一個是什麼都不懂,不小心關掉的人;第二個是很懂,非常非常在乎安全,或想亂搞的人。 我會和客戶說,這兩類的人,少之又少,又很難搞定,與其多花時間金錢來照顧它們寫替代程式,不如直接告訴它們請啟用javascript再來瀏覽此頁面。 方法很簡單:
在想強制使用javascript的頁面,<head>和</head>間,插入這段通用的tag

<noscript>
     <META HTTP-EQUIV="Refresh" CONTENT="0;URL=JS_Please.html">
</noscript>


如果有人瀏覽這頁面卻未啟用javascript,就會直接轉到JS_Please.html,接著在JS_Please.html上撰寫請啟用javascript的說明,或教不小心關掉的人要怎麼開啟它

後記:
php有個函式是get_browser,原以為是可以檢查瀏覽器所有的狀態,就很高興的拿來試試…沒想到只能得知瀏覽器是否有支援javascript,卻無法得知是否啟用;真是個讓人白高興一場的函式…
網路上還有很多檢查javascript是否啟用的方法和資訊,但大多複雜難用;用noscript來轉頁,是最簡便的方法。

2012年6月21日 星期四

ASP.NET Web Application 離線方式

這是我目前看ASP.NET中覺得最好的功能之一XD
在網站需要進行維護等需離線的行為時
為了避免讓User誤會而可以使用這個功能

只要在Application的根目錄底下建立App_Offline.htm 並將訊息寫在這個頁面即可
只要目錄底下有這個檔案
.NET就會自動將網頁導覽到這個頁面
如此就可以很方便的進行升級

要注意的事項則是網頁大小必須超過512k
且對於根目錄的權限要小心
以免被惡意寫入這個檔案而導致整個Application不起作用

2012年6月14日 星期四

固定ASP.NET程式開發伺服器的Port for web網站

在web application專案中可以使用專案工具控制([專案]>[屬性])
而如果只是建置時選擇web網站的話,則用以下方式解決

step1. 在visual studio的[工具]>[選項]>[專案和方案]>[一般]>取消"永遠顯示方案"
step2.[工具]>[外部工具]>[加入]>輸入以下設定
          標題: sever port:8080       <--隨便打
          命令:C:\Program Files (x86)\Common Files\microsoft shared\DevServer\9.0\
                      WebDev.WebServer.EXE
          引述:/port:8080/path:$(ProjectDir)\
step3.[工具]選項中就會出現剛才輸入的標題,執行以啟動固定的port
step4.在網站上按右鍵>[屬性頁]>[起始選項]>[使用自訂伺服器]>基礎URL輸入
          http://localhost:8080

測試:直接輸入http://localhost:8080/Default.aspx

SSIS 心得與小技巧

SSIS為Microsoft SQL Server裡Business Intelligence的一環
功能是整合各個不同來源的資料
以下環境處理使用SQL Server 2008
注意雖然SQL Server2012提供了轉換的功能來相容2008的檔案
但實際上還是很有可能會發生問題

 SSIS的檔案建立後會是dtsx檔
實際上裡面的內容是XML檔,由SSIS引擎進行實際操作
2008的版本有時候會發生問題是整個專案檔中作了變更
而dtsx檔卻沒跟著做正確變更,特別是在連線相關元件的連線改變時
有時候最好的情形甚至是將用到連線的元件砍掉重練
如果碰到設定應該沒問題卻總是收到SSIS的失敗訊息,那就應該考慮這個作法
假如遇到連線設定一直無法寫入連線元件(通常是連線的密碼)
可以考慮使用全域變數來規避,或是重新開啟SSIS試看看
在我接觸的經驗裡,連線設定是SSIS很弱的一個部分

SSIS開啟時會還原到上次處理時的內容
接著重新驗證載入內容裡面每一個處理過程,其中包含連線到裡面的資料來源
這樣往往使得SSIS在開啟過程花費大量時間而延誤處理
記得關閉SSIS前先關閉目前開啟的每一個dtsx檔

另外開啟SSIS的專案檔可以先做一個設定
上方工具列中的SSIS -> work offline
這樣可以避免SSIS在載入較大的檔案時卡在驗證的流程
當要用到取得網路上的資料時就取消work offine
雖然麻煩了些,但卻是省下時間的一招

雖然SSIS中有Script Component可以用來寫入程式運作
但在維護的考量上,部門鼓勵的處理方式是盡量用SSIS元件進行處理
使用元件能明顯的表達設計的意圖
雖然有時可能會較使用程式的方式來的麻煩,但帶來的好處應該是更多


以下紀錄曾碰過疑問的情境

1.重覆資料判斷處理

情境:想將重覆的資料判別出並寫進log,而不只是用sort工具將多出來的資料刪去
問題:如何判別資料是duplicate instances
解決:先利用derived column 新增欄位,再用aggregate元件裡的count物件來取得
            duplicate instance其數量,再將他寫入新欄位中,用conditional split處理

2.對應空值處理

情境:針對客戶給的資料做預防處理
問題:資料有可能不會有該欄位或該欄位無值
解決:conditional split裡 ISNULL(xxx) 和 TRIM(xxx) == "" 兩條件併用,
            TRIM()是預防給予的資料是數格空白

3.Thread

情境:欲建立物件來處理從data flow中經conditional split分支出來的資料
問題:物件在同時處理兩端不同走向的資料時可能會有multi-thread處理的問題
解決:先利用union all原件將所有要處理的資料先做統整,再用物件處理

4.Thread-2

情境:寫資料進error log碰到前端流程有太多錯誤資料要寫,使後端流程無法取得log檔
問題:手動加入lock控管機制也碰到lock object的取得也有thread上的問題
解決:經討論後直接濾掉前端的錯誤資料以避免整體流程發生錯誤
            實際上SSIS的線程管理是不常看到討論的一環,而且也容易有問題
            有這類需求時再考慮盡量避免掉資料存取衝突的可能,重新設計SSIS流程

2011年6月16日 星期四

Java Inner Class 記事

inner class的作用並不只是隱藏名稱或程式碼
他最大的效果是能有效及完整的達到"多重繼承"-實際上繼承多個non-interface
以下做深入的筆記

inner class的個用途是當建立一個inner class時
inner class可以存取外圍物件的所有元素而無需添加任何飾詞,如下範例:

interface Selector {
  boolean end();
  Object current();
  void next();
}   

public class Sequence {
  private Object[] items;
  private int next = 0;
  public Sequence(int size) { items = new Object[size]; }
  public void add(Object x) {
    if(next < items.length)
      items[next++] = x;
  }
  private class SequenceSelector implements Selector {
    private int i = 0;
    public boolean end() { return i == items.length; }
    public Object current() { return items[i]; }
    public void next() { if(i < items.length) i++; }
  }
  public Selector selector() {
    return new SequenceSelector();
  }   
  public static void main(String[] args) {
    Sequence sequence = new Sequence(10);
    for(int i = 0; i < 10; i++)
      sequence.add(Integer.toString(i));
    Selector selector = sequence.selector();
    while(!selector.end()) {
      System.out.print(selector.current() + " ");
      selector.next();
    }
  }
} /* Output:
0 1 2 3 4 5 6 7 8 9
*/

 範例的inner class的SequenceSelector可以存取其外部的Sequence的類別成員
不論inner class內放置的層次有多深
所有的outer classes的成員,即使是private修飾也都可以被他存取

inner class必須記錄一個reference指向enclosing class(外圍類別)
當取用外部成員時,編譯器會自動用那個隱藏的reference取得
也因為inner class物件產生時必須要關聯至其enclosing class的某個物件
建構inner class物件的同時必須要有enclosing class的物件

inner class可以寫在程式的任意位置
這不是說他們只會在執行那個段落時產生
他們一樣會跟其他class一起被編譯出來,只是在該段落以外無法被使用

假如不需要inner class物件和enclosing class物件之間的連結關係
可以將inner class宣告為static,而這種class被稱為nested class
nested class在生成物件時不需要外圍class的物件,也無法存取外圍class的非靜態物件
一般的inner class不能擁有static data、static fields或nested class,但nested class都可以有

一般而言,interface內不能加入任何程式碼,但nested class卻可以是interface的一部分
任何加到interface的class會自動成為public和static
甚至可以在inner class中時做外圍的interface
如果想寫出讓某interface的不同實作都可以共用的程式碼時可以這麼做,如下:

public interface ClassInInterface {
  void howdy();
  class Test implements ClassInInterface {
    public void howdy() {
      System.out.println("Howdy!");
    }
    public static void main(String[] args) {
      new Test().howdy();
    }
  }
} /* Output:
Howdy!
*/

繼承inner class則必須注意要先建立外圍類別
指向outer class的reference也必須在建構式由我們建立,如下:

class WithInner {
  class Inner {}
}

public class InheritInner extends WithInner.Inner {
  //! InheritInner() {} // Won't compile
  InheritInner(WithInner wi) {
    wi.super();
  }
  public static void main(String[] args) {
    WithInner wi = new WithInner();
    InheritInner ii = new InheritInner(wi);
  }
}