Roslyn



MS에서 발표한 RoadMap에 따라 예고 되었던 로슬린 개발 진행이 표면에 나타나고 있습니다.


우선 관련 URL을 알려 드리도록 하겠습니다.


Roslyn HomePage

Roslyn Overview



저에게 로슬린(Roslyn)이라고 하면 떠 오르는 관련 단어가 아래와 같습니다.


- 동적 컴파일, javascript, script language,  CodeDom, dynamic


이 키워드로 Ryslyn을 알려 드리기에는 미흡하지만 컨셉은 전해줄 수 있을것이라 생각합니다.


간단하게 설명을 드리자면 운영 환경에서 프로그래머의 도움없이 동적으로 수정된 코드를 사용할 수 있도록 지원해주는 Host를 설정하고 사용하는 것입니다.


아래 간단한 예제를 보여 드리겠습니다.

void EngineTest()
{
    var scriptEngine = new ScriptEngine();
    scriptEngine.Execute("using System; Console.WriteLine(DateTime.Now);");
}

위 메소드를 호출하여 실행해 보면 정상적으로 실핼되는 것을 확인할 수 있습니다.

(Roslyn homepage에서 관련 설치 파일을 설치하고 진행해야 정상적으로 동작합니다.)


또 다른 예제

아래 코드는 네임스페이스를 SyntaxTree를 이용해서 수정하는 예제다.

class Program
{
    static void Main(string[] args)
    {
        //코드 텍스트로 Syntax tree에 넣기
        SyntaxTree syntaxTree = SyntaxTree.ParseCompilationUnit(
            @"using System;
            namespace Sample
            {
                class Class1
                {
                }
            }");
 
        //Sample 네임스페이스를 RoslySample로 수정을 위한 루트 가져오기
        var root = (CompilationUnitSyntax)syntaxTree.Root;
 
        //현재 네임스페이스 가져오기
        var currentNamespace = (NamespaceDeclarationSyntax)root.Members[0];
 
        //새로운 네임 스페이스 수정 코드
        var newNameSpace = currentNamespace.Update(currentNamespace.NamespaceKeywordSyntax.IdentifierName("RoslynSample"), currentNamespace.OpenBraceToken,
                                                    currentNamespace.ExternscurrentNamespace.UsingscurrentNamespace.Members,
                                                    currentNamespace.CloseBraceTokencurrentNamespace.SemicolonTokenOpt);
 
        //RoslynSample로 수정된 코드 노드를 업데이트 한다.
        root = root.ReplaceNode(currentNamespacenewNameSpace);
    }
}

이와 같이 작성된 코드에 대해서도 이후에 수정을 임의로 할 수 있고, 이 과정으로 인해 올바르게 작성이 되었는지 여부나 자동 refactoring을 해주는 서비스를 해 줄 수도 있을 것이다.


관련 URL : Example #1


C# DataSet 압축


DataSet, DataTable(이하 DataSet으로만 언급하겠지만 DataTable도 같은 사항이다)을 무척이나 많은 용도로 사용하고 있다. DB 리턴값으로 사용하고 네트워크로 통해 넘겨서 클라이언트에서 사용할 수 있도록 한다.


그렇지만 많의 양의 DataSet을 옮기는 가운데 Loading time이 발생하게 된다. 더군다나 네트워크를 통해서 데이타를 보낼때에 더 심각한 UI 멈춤 현상이 발생 할 수도 있다. 그렇지만 네트워크로 조금이라도 압축된 상태로 줄어 들어 넘어 온다면 리드타임을 줄이는 도움이 돌 것으로 기대하며 Dataset압축에 대해서 알아보자.



아래는 DataSet 압축과 압축해제에 관한 소스 전체이다.

/// <summary>
/// DataSet 압축
/// </summary>
/// <param name="ds">압축할 DataSet</param>
/// <param name="remortingFormat">SerializationFormat 종류 default : Binary</param>
/// <returns>string으로 변환해서 반환 한다.</returns>
/// <remarks>
/// DataSet에 대해서 압축 한다.
/// 네트워크 동신에서 많은 양을 압축하여 보낼 때 사용 하면 된다.
/// </remarks>
/// <example>
/// <code> DataSet 압축
/// CompressDataSet(DataSet);
/// </code>
/// </example>
/// <exception cref="InvalidCastException">
/// </exception>
/// <exception cref="ArgumentException">
/// </exception>
public string CompressDataSet(DataSet dsSerializationFormat remortingFormat = SerializationFormat.Binary)
{
    var compressedByte = CompressDataSetBytes(dsremortingFormat);
    return Convert.ToBase64String(compressedByte);
}
 
/// <summary>
/// DataSet 압축
/// </summary>
/// <param name="ds">압축할 DataSet</param>
/// <param name="remortingFormat">SerializationFormat 종류 default : Binary</param>
/// <returns>byte[] 변환해서 반환 한다.</returns>
/// <remarks>
/// DataSet에 대해서 압축 한다.
/// 네트워크 동신에서 많은 양을 압축하여 보낼 때 사용 하면 된다.
/// </remarks>
/// <example>
/// <code> DataSet을 압축한다.
/// CompressDataSet(DataSet);
/// </code>
/// </example>
/// <exception cref="InvalidCastException">
/// </exception>
/// <exception cref="ArgumentException">
/// </exception>
public byte[] CompressDataSetBytes(DataSet dsSerializationFormat remortingFormat = SerializationFormat.Binary)
{
    #region Serialize
    ds.RemotingFormat = remortingFormat;
    BinaryFormatter bf = new BinaryFormatter();
    byte[] inSerializeByte = null;
    using (MemoryStream ms = new MemoryStream())
    {
        bf.Serialize(msds);
        inSerializeByte = ms.ToArray();
    }
    #endregion
 
    byte[] outPut = BytesCompress(inSerializeByte);
            
    return outPut;
}
 
/// <summary>
/// byte[]를 압축한다.
/// </summary>
/// <param name="compressByte">압축할 byte[] 데이타</param>
/// <returns>byte[] 압축된 데이타를 반환 한다..</returns>
/// <seealso cref="http://msdn.microsoft.com/ko-kr/library/system.io.compression.deflatestream.aspx"/>
/// <remarks>
/// byte[]를 DeflateStream을 이용해서 압축한다.
/// </remarks>
/// <example> byte[]를 압축한다.
/// <code>
/// BytesCompress(byte[] compressByte);
/// </code>
/// </example>
/// <exception cref="InvalidCastException">
/// </exception>
/// <exception cref="ArgumentException">
/// </exception>
public byte[] BytesCompress(byte[] compressByte)
{
    #region compress
    byte[] outPut = null;
    using (MemoryStream outStream = new MemoryStream())
    {
        using (DeflateStream objZS = new DeflateStream(outStreamCompressionMode.Compress))
        {
            objZS.Write(compressByte0compressByte.Length);
            objZS.Flush();
            objZS.Close();
        }
        outPut = outStream.ToArray();
    }
    #endregion
 
    return outPut;
}
 
/// <summary>
/// DataSet을 압축 해제 한다.
/// </summary>
/// <param name="compressedDsStr">압축 해제할 데이타 string</param>
/// <param name="remortingFormat">SerializationFormat 종류 default : Binary</param>
/// <returns>압축 해제한 DataSet를 반환 한다.</returns>
/// <remarks>
/// 이 메소드는 압축된 DataSet의 Byte[] -> string이 입력해야 정상적으로 동작한다.
/// </remarks>
/// <example> DataSet 압축 해제(string)
/// <code>
/// byte[] compressedByte = Convert.FromBase64String(compressedDsStr);
/// DecompressDataSet(compressedByte, remortingFormat);
/// </code>
/// </example>
/// <exception cref="InvalidCastException">
/// </exception>
/// <exception cref="ArgumentException">
/// </exception>
public DataSet DecompressDataSet(string compressedDsStrSerializationFormat remortingFormat = SerializationFormat.Binary)
{
    byte[] compressedByte = Convert.FromBase64String(compressedDsStr);
    return DecompressDataSet(compressedByteremortingFormat);
}
 
/// <summary>
/// DataSet을 압축 해제 한다.
/// </summary>
/// <param name="compressedBytes">압축 해제할 데이타 DataSet byte[]</param>
/// <param name="remortingFormat">SerializationFormat 종류 default : Binary</param>
/// <returns>압축 해제한 DataSet를 반환 한다.</returns>
/// <seealso cref="http://msdn.microsoft.com/ko-kr/library/system.data.dataset.aspx"/>
/// <remarks>
/// 이 메소드는 압축된 DataSet의 Byte[]를 입력해야 정상적으로 동작한다.
/// </remarks>
/// <example> 
/// <code> 압축된 DataSet의 byte[]를 압축 해제 한다.
/// byte[] compressedByte = Convert.FromBase64String(compressedDsStr);
/// DecompressDataSet(compressedBytes, remortingFormat);
/// </code>
/// </example>
/// <exception cref="InvalidCastException">
/// </exception>
/// <exception cref="ArgumentException">
/// </exception>
public DataSet DecompressDataSet(byte[] compressedBytesSerializationFormat remortingFormat = SerializationFormat.Binary)
{
    var outMs = BytesDeCompress(compressedBytes);
 
    #region Deserialize
    DataSet outDs = new DataSet();
    outDs.RemotingFormat = remortingFormat;
 
    BinaryFormatter bf = new BinaryFormatter();
    outDs = (DataSet)bf.Deserialize(outMsnull);
    #endregion
    return outDs;
}
 
/// <summary>
/// byte[] 압축 해제 한다.
/// </summary>
/// <param name="compressedBytes">압축 해제할 byte[]</param>
/// <returns>압축 해제된 byte[]를 반환 한다.</returns>
/// <seealso cref="http://msdn.microsoft.com/ko-kr/library/system.io.compression.deflatestream.aspx"/>
/// <remarks>
/// byte[]에 대해서 DeflateStream으로 압축 해제 한다.
/// 파라메터는 압축된 byte[] 넘겨야 정상 동작 한다.
/// </remarks>
/// <example> byte[]를 압축 해제 한다.
/// <code>
/// var compressBytes = BytesCompress(byte[]);
/// var outMs = BytesDeCompress(compressBytes);
/// </code>
/// </example>
/// <exception cref="InvalidCastException">
/// </exception>
/// <exception cref="ArgumentException">
/// </exception>
public Stream BytesDeCompress(byte[] compressedBytes)
{
    #region UnCompress
    MemoryStream inMs = new MemoryStream(compressedBytes);
 
    MemoryStream outMs = null;
    using (DeflateStream zipStream = new DeflateStream(inMsCompressionMode.Decompresstrue))
    {
        byte[] buffer = new byte[32768];
        outMs = new MemoryStream();
 
        //Stream의 모든 데이타를 복사 한다.
        while (true)
        {
            int read = zipStream.Read(buffer0buffer.Length);
            if (read <= 0)
                break;
            outMs.Write(buffer0read);
        }
 
        outMs.Flush();
        outMs.Seek(00);   //재 사용을 위해 처음으로 되돌린다.
 
        zipStream.Flush();
        zipStream.Close();
    }
    #endregion
 
    return outMs;
}



(소스 코드 자체에 주석과 직관적인 코딩으로 충분히 파악이 가능할 것으로 예상하므로 별도의 설명을 생략)
※ 장황한 설명 보다는 주석과 소스코드 자체 만으로도 이해할 수 있도록 포스팅을 하는데 주안점을 두도록 하였습니다. 실제 개발에서 필요한 소스는 단순히 Copy & Paste 만으로도 사용할 수 있도록 노력할 것이며 주석을 이용해 nDoc이나 별도의 자동 DOC 제작 유틸로 API 문서를 만드는 데에도 도움이 되었으면 한다.
DOC에 대한 프로그램 정보 정보 Util link


위 코드에 대해서 사용법과 확인 사항을 테스트 메소드를 통해서 알려 주도록 하겠다.

[TestMethod] public void DataSet_Compress_DeCompress_TestMethod() {     //테스트할 DataSet 생성     DataSet ds = new DataSet();     DataTable dt = new DataTable();     ds.Tables.Add(dt);     dt.Columns.Add(new DataColumn("column1"));     dt.Columns.Add(new DataColumn("column2"));     dt.Columns.Add(new DataColumn("column3"));     dt.Columns.Add(new DataColumn("column4"));     for (int i = 0i < 100i++)     {         var row = dt.NewRow();         row[0= "value1밸류";         row[1= "value2밸류";         row[2= "value3밸류";         row[3= "value4밸류";         dt.Rows.Add(row);     }     var compressDs = CompressDataSetBytes(ds);          //DataSet을 압축하여 byte[]로 반환 테스트     var temp1 = Convert.ToBase64String(compressDs);     //string으로 변환     var temp2 = DecompressDataSet(temp1);               //변환된 string이 정상적으로 압축 해제되는지 테스트     var deCompressDs = DecompressDataSet(compressDs);   //압축된 byte[]를 압축 해제 테스트     #region 검증 작업     bool isValid = true;     foreach (DataRow dr in deCompressDs.Tables[0].Rows)     {         if ("value1밸류" == dr[0].ToString() && "value2밸류" == dr[1].ToString() && "value3밸류" == dr[2].ToString() && "value4밸류" == dr[3].ToString())         {             continue;         }         else         {             isValid = false;             break;         }     }     #endregion     Assert.IsTrue(isValid); //검증 완료 }

실행 시켜 보면 위 코드가 정상적으로 테스트를 통과 하는 것을 볼 수 있을 것이다.



위 부분에서 만든 메소드 중에 DataSet에 대해서만 압축과 해제를 하도록 하지 않는 부분이 있다.

- byte[] BytesCompress(byte[] compressByte)

- Stream BytesDeCompress(byte[] compressedBytes)


이 두 메소드를 이용하면 일반적인 object도 압축과 해제를 할 수 있을것으로 예상하나 아직 테스트를 해보지 안았다. 다음 포스트에 테스트한 것을 올려 보도록 하겠다.






C# MemoryStream Compression


Code Project에서 올라온 Memory기반 압축 Util이다.


http://www.codeproject.com/Articles/6834/MemoryStream-Compression


관련 글 : 2013/02/15 - [.Net Framework] - [Compress] GZipStream - 문자열 압축과 해제



사용 방법은 아래와 같다.


//압축 사용 법 using ICSharpCode.SharpZipLib.BZip2; BZip2OutputStream zosCompressed = new BZip2OutputStream(msCompressed); string sBuffer = "This represents some data being compressed."; //압축 해제할 문자열 byte[] bytesBuffer = Encoding.ASCII.GetBytes(sBuffer); zosCompressed.Write(bytesBuffer, 0, bytesBuffer.Length); zosCompressed.Finalize(); zosCompressed.Close(); bytesBuffer = msCompressed.ToArray(); string sCompressed = Encoding.ASCII.GetString(bytesBuffer); //결과값 : QlpoOTFBWSZTWZxkIpsAAAMTgEABBAA+49wAIAAxTTIxMTEImJhNNDIbvQaWyYEHiwN49LdoKNqKN2C9ZUG5+LuSKcKEhOMhFNg=



//압축 해제 사용 법
MemoryStream msUncompressed = 
    new MemoryStream(Encoding.ASCII.GetBytes(sCompressed));
BZip2InputStream zisUncompressed = new BZip2InputStream(msUncompressed);
bytesBuffer = new byte[zisUncompressed.Length];
zisUncompressed.Read(bytesBuffer, 0, bytesBuffer.Length);
zisUncompressed.Close();
msUncompressed.Close();
string sUncompressed = Encoding.ASCII.GetString(bytesBuffer);


자세한 사항은 해당 링크에서 확인해 보기를 바라며 소스도 같이 다운로드 받을 수 있을 것이다.





C# GZipStream 문자열 압축과 해제



많은 문자열을 네트워크를 통해 보낼때 CPU 비용 보다는 네트워크 비용을 줄이기 위해 사용할 수 있을것이다. 하지만 압축과 해제는 CPU뿐만 아니라 메모리에서도 제약 사항을 많이 받는다. 최대 4GB까지 지원하지만 압축된 데이터를 저장할 수 있는 메모리 공간과 압축 해제된 데이타를 저장할 수 있는 공간이 필요 하기에 예상보다 많은 메모리 사용량이 필요 할 수 있다. 그러르모 언제든지 OutOfMemoryException(OOM)이 발생할 수 있으며, InSufficientMemoryException 또한 발생할 수 있다. 그래서 실 사용에서는 사용환경에 맞게 최대 허용가능한 용량을 산정하여 사용할 필요가 있다.


실예로 모 사이트에서 수만 row의 값을 압축하여 클라이언트에 내려 보냈으나 압축해제시 OutofMemoryException이 발생하여 고생을 했던적이 있다. 그리고 서버상에서는 여러 사용자가 요청할 수도 있는 환경이기에 더욱더 주의해서 사용해야 한다.


아래는 실제로 메모리 상에서 문자열을 압축하고 해제하는 코드다.


압축 메소드

/// <summary>
/// 중복된 많은 문자열을 용량을 줄일 때 사용
/// , 메모리가 많이 사용되기 때문에 충분히 테스트가 되어야 하며 사전에 사용할 수 있는 최대값을 결정하고 허용된 범위 안에서만 되도록 한다.
/// </summary>
/// <param name="str">압축할 문자열</param>
/// <returns>압축된 문자열</returns>
/// <remarks>
/// UTF-8기반 문자열 압축 - 최대 4Gb 이하만 사용
/// , 메모리가 많이 사용되기 때문에 충분히 테스트가 되어야 하며 사전에 사용할 수 있는 최대값을 결정하고 허용된 범위 안에서만 되도록 한다.
/// </remarks>
/// <example> 문자열 압축
/// <code>
/// string str = "compress string";
/// string compressed = Compression(str);
/// </code>
/// </example>
/// <exception cref="OutOfMemoryException"></exception>
/// <exception cref="InsufficientMemoryException">사용 가능한 메모리가 없을때 발생</exception>
public static string Compression(string str)
{
    var rowData = Encoding.UTF8.GetBytes(str);
    byte[] compressed = null;
    using (var outStream = new MemoryStream())
    {
        using (var hgs = new GZipStream(outStreamCompressionMode.Compress))
        {
            //outStream에 압축을 시킨다.
            hgs.Write(rowData0rowData.Length);
        }
        compressed = outStream.ToArray();
    }
 
    return Convert.ToBase64String(compressed);
}


압추해제 메소드

/// <summary>
/// 문자열 압축 해제
/// , 메모리가 많이 사용되기 때문에 충분히 테스트가 되어야 하며 사전에 사용할 수 있는 최대값을 결정하고 허용된 범위 안에서만 되도록 한다.
/// </summary>
/// <param name="compressedStr">압축된 문자열</param>
/// <returns>평문 문자열</returns>
/// <remarks>
///  UTF-8기반 문자열 압축 해제 - 최대 4Gb 이하만 사용
///  , 메모리가 많이 사용되기 때문에 충분히 테스트가 되어야 하며 사전에 사용할 수 있는 최대값을 결정하고 허용된 범위 안에서만 되도록 한다.
/// </remarks>
/// <example> 문자열 압축 해제
/// <code>
/// string deCompressed = DeCompression(compressedStr); //압축 해제된 문자열
/// </code>
/// </example>
/// <exception cref="OutOfMemoryException"></exception>
/// <exception cref="InsufficientMemoryException">사용 가능한 메모리가 없을때 발생</exception>
public static string DeCompression(string compressedStr)
{
    string output = null;
    byte[] cmpData = Convert.FromBase64String(compressedStr);
    using (var decomStream = new MemoryStream(cmpData))
    {
        using (var hgs = new GZipStream(decomStreamCompressionMode.Decompress))
        {
            //decomStream에 압축 헤제된 데이타를 저장한다.
            using (var reader = new StreamReader(hgs))
            {
                output = reader.ReadToEnd();
            }
        }
    }
 
    return output;
}


(소스 코드 자체에 주석과 직관적인 코딩으로 충분히 파악이 가능할 것으로 예상하므로 별도의 설명을 생략)
※ 장황한 설명 보다는 주석과 소스코드 자체 만으로도 이해할 수 있도록 포스팅을 하는제 주안점을 두도록 하겠습니다.
  진짜로 필요한 소스는 단순히 Copy & Paste만드르도 사용할 수 있도록 노력할 것이며 주석을 nDoc이나 별도의 자동 DOC 제작 유틸로 API 문서를 만드는 데에도 도움이 되었으면 한다.
DOC Util link



다음은 위 메소드가 정상적으로 동작하는지 테스트를 해 보도록 하겠다.

[TestMethod]
public void Compress_DeCompress_TestMethod()
{
    string str = "양복 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press";
    string compressed = Compression(str);
    int fooLength = compressed.Length;      // 60   - 약 90% 정도가 압축이 되었다.
    int barLength = str.Length;             // 704
    string deCompressed = DeCompression(compressed);
    int bazLength = deCompressed.Length;    // 704
 
    //System.IO.BinaryWriter
    //System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
 
    Assert.AreEqual(strdeCompressed);
}

(위 테스트는 중복된 문자열이 많게 하여 테스트를 진행하였다.)


압축과 해제를 적절한 곳에 제한적으로 사용한다면 훌륭한 모듈이지만 과도하게 적용하여 사용하다가 보면 CPU나 메모리 제한으로 언제 어떠한 일이 발생할지 모른다. 그러므로 적절히 제한적으로 사용하여 시스템의 전체적인 퍼포먼스를 제대로 사용할 수 있는 개발자가 되었으면 한다.


Tip!

1. 압축에 관계된 오픈 소스가 여러가지 존재 하고 있다.

.Net Framework에서 기본제공 하는 속도나 메모리 제약 사항을 뛰어 넘는 훌륭한 솔루션들이 많이 있지만 제한에 대한 높이만 다를뿐 언제든지 이와 같은 문제에 직면할 수 있으므로 주의 해서 사용 해야 한다.


2. 다음에는 문자열만 압축하는 것이 아닌 Object 자체 까지도 압축할 수 있는 방법을 알아 보도록 하겠다.


C# FileSystemWatcher 디렉토리, 파일 변경 모니터링



이번 포스트에서는 System.IO 네임스페이스에 위치하는 FileSystemWatcher 클래스에 대해서 알아 보도록 하겠다.

    FileSystemWatcher watch = null;
    watch = new FileSystemWatcher(Path.Combine(Environment.CurrentDirectory"folder"), "*.DLL");   //실행되는 폴더의 folder이라는 하위 폴더에서 DLL 파일만 변경에 대해서 모니터링 세팅
    watch.EnableRaisingEvents = true;   //이벤트 활성화 시킴
    watch.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.Attributes | NotifyFilters.DirectoryName | NotifyFilters.FileName | NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.Security | NotifyFilters.Size;
 
    watch.Changed += (se=> { Console.WriteLine("Changed"); };
    watch.Created += (se=> { Console.WriteLine("Created"); };
    watch.Deleted += (se=> { Console.WriteLine("Deleted"); };
    watch.Renamed += (se=> { Console.WriteLine("Renamed"); };
 
    Console.ReadKey();

위와 코드를 실행하고 'folder'폴더 안에 있는 dll파일을 추가 하거나 수정하면 해당 이벤트가 


전체적으로 어렵지 않게 사용할 수 있어서  주석과 직관적인 코드로 설명을 대체 하도록 하겠다.


아래는 NotifyFilter의 속성에 대한 설명이다.

멤버이름 설명
Attributes 파일 또는 폴더의 특성
CreateTime 파일 또는 폴더를 만든 시간
DirectoryName 디렉토리 이름
FileName 파일이름
LastAccess 파일 또는 폴더를 마지막으로 열었던 날짜
LastWrite 파일 또는 폴더에 마지막으로 기록된 날짜
Security 파일 또는 폴더의 보안 설정
Size 파일 또는 폴더의 크기


Tip!

NotifyFilter을 적절하게 세팅하고 발생하는 이벤트도 관리해서 중복되는 이벤트가 발생하지 않도록 해야 하며 중복 발생에 대한 처리도 해서 실질적으로 사용해야 하겠다.


C# Delegate


Delegate는 인자만 맞으면 실행되게 하는 대신 실행 시켜주는 Managed C# point 역할을 하는 특별한 객체이다. 이번 포스트에서는 묶여진 Delegation을 풀어 따로 결과값을 받아 오는 방안에 대해서 알아 보도록 하겠다.

아래와 같이 메소드를 등록(간단하게 Action 으로 진행)하고 마지막에 인자를 넘겨 실행 시키면 전체 연결된 체인 메소드가 실행이 된다.

            TestDelegate testDelegate = null;
 
            testDelegate += (ab=> { return (a + b); };
            testDelegate += (ab=> { return (a - b); };
            testDelegate += (ab=> { return (a / b); };
            testDelegate += (ab=> { return (a * b); };
 
            Debug.WriteLine(testDelegate(1020));

하지만 위와 같이 실행하면 각각의 결과에 대한 값이 나오는 것이 아니라 최종 마지막에 실행될 '*' 연산 결과에 대해서만 화면에 뿌려주게 된다. 전에 실행된 결과값은 메모리상에서 사라지게 되는 것이다.

이번에는 위와 같이 체인으로 묶여 있어도 각각의 결과값에 대해서 사용할 수 있도록 만든 메소드를 소개 하겠다.
        /// <summary>

        /// MulticastDelegate를 이용해 여러 메소드를 실행 할 때 각각의 반환값을 받아 온다.         /// </summary>         /// <typeparam name="T">return object type</typeparam>         /// <param name="func">Delegate[]를 반환하는 Func</param>         /// <param name="args">delegate가 실행에 필요한 인자 값</param>         /// <returns>delegate가 실행되고 반환된 리턴값들</returns>         /// <remarks>         /// 동적인 delegate의 결과값을 IEnumerable<T>로 받아 오기         /// </remarks>         /// <exception cref="ArgumentNullException">         /// Null 을 넘겨 주면 발생         /// </exception>         /// <exception cref="InvalidCastException">         /// Generic가 제대로 선언되지 않으면 발생         /// </exception>         /// <example>         /// delegate int TestDelegate(int a, int b);         /// TestDelegate testDelegate = null;         /// testDelegate += (a, b) => { return (a + b); };         /// testDelegate += (a, b) => { return (a - b); };         /// testDelegate += (a, b) => { return (a / b); };         /// testDelegate += (a, b) => { return (a * b); };         /// GetDelegateInvokeResults<int>(() => delegate.GetInvocationList(), 1, 2);   //호출시 generic 방법으로 반환될 형식을 정의 함.         /// </example>                  public IEnumerable<T> GetDelegateInvokeResults<T>(Func<Delegate[]> funcparams object[] args)         {                          List<T> list = new List<T>();             //인자로 받은 Func를 실행한다.             var delList = func.Invoke();             //Delegate[]를 loop             foreach (var del in delList)             {                 //동적 인자로 실행하고 반환값을 List에 넣는다.                 list.Add((T)del.DynamicInvoke(args));             }             return list;         }

delegate를 동적으로 컨트롤 하기 위해서 Func와 params 라는 개념을 도입을 하여 메소드를 만들었다.


위 메소드를 아래와 같이 사용하여 results에 담아 올 수 있다.

TestDelegate testDelegate = null;         testDelegate += (ab=> { return (a + b); };         testDelegate += (ab=> { return (a - b); };         testDelegate += (ab=> { return (a / b); };         testDelegate += (ab=> { return (a * b); };         var results = GetDelegateInvokeResults<int>(() => testDelegate.GetInvocationList(), 1020);

이렇게 되면 결과값을 가지고 지지고 볶을 수 있을 것이다.


다음에 기회가 된다면 일률적인 값으로 파라메터를 넘기는 것이 아닌 각각의 delegate마다 다른 파라메터를 넘길 수 있는 방안을 알아 보도록 하겠다.

What?!? C# Could Do That??? from Øredev Conference on Vimeo.

MVVM Applied in Windows Phone and Windows 8 from Øredev Conference on Vimeo.

+ Recent posts