잊지 않겠습니다.

Hyper-V 에서 Network Usage와 Process Usage를 얻어내기 위해서 WMI 뒤지기 삽질. -_-

이번에 WMI를 보면서 많은 생각이 든 것이, MS가 참 대단하다는 생각이 든다. 정말 여러 데이터들을 잘 모아뒀다는 그런 느낌이라고 해야지 될까. 
먼저 Performance Counter에 사용되는 WMI 들은 Win32_PerfFormattedData_, Win32_PerfRawData_ 두가지로 나뉘어진다. 이 두개의 데이터의 차이는 좀 더 알아보기 쉽게 변형된 데이터와 Raw Data의 차이를 가지고 있다. 예를 들어 Network Usage의 경우 FormattedData의 경우에는 Bytes/sec의 정보가 나온다. 그렇지만 Raw Data는 Bytes의 누적값으로 표현이 된다. 이렇게 약간약간 다른 값들이 있기 때문에, 좀 더 세밀하게 볼 필요성이 있다. 

뭐.. 바로 본론으로 들어가면, 필요한 WMI 정보와 WMI를 얻어내는데 필요한 Helper Class를 먼저 제작해줬다. 


public class WmiHelper
{
    private readonly ConnectionOptions _connectionOptions;
    public WmiHelper()
    {
        _connectionOptions = new ConnectionOptions()
                                {
                                    Impersonation = ImpersonationLevel.Impersonate,
                                    Username = Configure.UserName,
                                    Password = Configure.Password,
                                    Authentication = AuthenticationLevel.Default,
                                    EnablePrivileges = true

                                };
    }

    protected ManagementObject GetManagementSingleObject(string wmiQuery, string wmiNamespace)
    {
        ManagementScope scope = new ManagementScope(wmiNamespace, _connectionOptions);
        try
        {
            scope.Connect();
            ObjectQuery query = new ObjectQuery(wmiQuery);
            using(ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query))
            {
                ManagementObjectCollection queryCollection = searcher.Get();
                return GetFirsManagementObject(queryCollection);
            }
        }
        catch(Exception ex)
        {
            Console.WriteLine(ex.Message);
            return null;
        }
    }

    protected ManagementObjectCollection GetManagementCollection(string wmiQuery, string wmiNamespace)
    {
        ManagementScope scope = new ManagementScope(wmiNamespace, _connectionOptions);
        try
        {
            scope.Connect();
            ObjectQuery query = new ObjectQuery(wmiQuery);
            using(ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query))
            {
                ManagementObjectCollection queryCollection = searcher.Get();
                return queryCollection;
            }
        }
        catch(Exception ex)
        {
            Console.WriteLine(ex.Message);
            return null;
        }
    }

    protected ManagementObject GetFirsManagementObject(ManagementObjectCollection managementObjectCollection)
    {
        if(managementObjectCollection.Count == 0)
        {
            return null;
        }
        else
        {
            var enumerator = managementObjectCollection.GetEnumerator();
            enumerator.MoveNext();
            return (ManagementObject)enumerator.Current;
        }
    }
}
WmiHelper는 Connection과 WMI Query를 Execute, 얻어진 ManagementObject와 ObjectCollection 얻어오는 일을 담당한다.
이제 Hyper-V의 Network Usage와 CPU Usage를 얻어오는 PerformanceCounter Class의 내용은 다음과 같다. 

public class VirtualMachinePerformanceCounter : WmiHelper
{
    //public long NetworkReadBytes { get; private set; }
    //public long NetworkWriteBytes { get; private set; }
    //public double CpuUsage { get; private set; }

    private readonly string _vmName;
    private readonly string _hostName;

    public VirtualMachinePerformanceCounter(string vmName, string hostName)
    {
        _vmName = vmName;
        _hostName = hostName;
    }

    public NetworkBandwidth MonitorNetworkBandwidth()
    {
        string wmiNamespace = string.Format("\\\\{0}\\Root\\cimv2", _hostName);
        string vmComputerName = GetVirtualComputerSystemGuidName();
        string wmiQuery =
            string.Format(
                "SELECT * FROM Win32_PerfRawData_NvspNicStats_HyperVVirtualNetworkAdapter WHERE Name like \"%_{0}--%\"",
                vmComputerName);
 
        ManagementObject managementObject = GetManagementSingleObject(wmiQuery, wmiNamespace);
        if(managementObject != null)
        {
            string recevedBytesString = managementObject["BytesReceivedPersec"].ToString();
            string sentBytesString = managementObject["BytesSentPersec"].ToString();
            string totalBytesString = managementObject["BytesPersec"].ToString();

            NetworkBandwidth networkBandwidth = new NetworkBandwidth(long.Parse(totalBytesString),
                                                         long.Parse(recevedBytesString), long.Parse(sentBytesString));
            Console.WriteLine("{0}\t{1}\t{2}", totalBytesString, recevedBytesString, sentBytesString);
            return networkBandwidth;
        }
        else
        {
            return NetworkBandwidth.Empty;
        }
    }


    public CpuUsage MonitorCpuUsage()
    {
        /*
        Win32_PerfRawData_HvStats_HyperVHypervisorVirtualProcessor
        Frequency_PerfTime
        PercentGuestRunTime
        Timestamp_PerfTime
        */
        string wmiNamespace = string.Format("\\\\{0}\\Root\\cimv2", _hostName);
        string wmiQuery = string.Format("SELECT Frequency_PerfTime,PercentGuestRunTime, Timestamp_PerfTime " +
                                        "FROM Win32_PerfRawData_HvStats_HyperVHypervisorVirtualProcessor " +
                                        "WHERE Name like \"{0}:%\"", _vmName);

        ManagementObjectCollection preManagementObjects = GetManagementCollection(wmiQuery, wmiNamespace);
        
        int logicalProcessCount = preManagementObjects.Count;

        if(logicalProcessCount == 0)
        {
            return CpuUsage.Empty;
        }
        else
        {
            ManagementObject m1 = GetFirsManagementObject(preManagementObjects);
            long frequencyPerfTime = long.Parse(m1["Frequency_PerfTime"].ToString());

            long percentGuestRunTime1 = long.Parse(m1["PercentGuestRunTime"].ToString());
            long timestampPerfTime1 = long.Parse(m1["Timestamp_PerfTime"].ToString());

            System.Threading.Thread.Sleep(1000);

            ManagementObject m2 = GetManagementSingleObject(wmiQuery, wmiNamespace);
            long percentGuestRunTime2 = long.Parse(m2["PercentGuestRunTime"].ToString());
            long timestampPerfTime2 = long.Parse(m2["Timestamp_PerfTime"].ToString());

            CpuUsage usage = new CpuUsage()
                                 {
                                     LogicalProcessCount = logicalProcessCount,
                                     FrequencyPerfTime = frequencyPerfTime,
                                     GuestRunTime1 = percentGuestRunTime1,
                                     GuestRunTime2 = percentGuestRunTime2,
                                     TimeStampPerTime1 = timestampPerfTime1,
                                     TimeStampPerTime2 = timestampPerfTime2
                                 };

            return usage;
        }
    }

    private string GetVirtualComputerSystemGuidName()
    {
        string wmiNamespace = string.Format("\\\\{0}\\Root\\virtualization", _hostName);
        string wmiQuery = string.Format("SELECT * FROM Msvm_ComputerSystem WHERE elementname = \"{0}\"", _vmName);

        ManagementObject managementObject = GetManagementSingleObject(wmiQuery, wmiNamespace);
        return (string)managementObject["Name"];
    }
}
간단히 코드 설명을 한다면, 

Network Usage는 Win32_PerfRawData_NvspNicStats_HyperVVirtualNetworkAdapter에서 얻어지는 값을 이용하면 된다. 
VM과 연관을 찾기 위해서는 root\virtualization에서 VirtualComputerSystem의 Name 값을 이용해서 like 검색을 하면 된다.

CPU Usage의 경우에는 http://blogs.msdn.com/tvoellm/archive/2008/07/14/how-to-get-processor-utilization-for-hyper-v-via-wmi.aspx를
참고.

* WmiHelper를 상속받는 것이 아닌, 아무래도 생성해서 Instance를 private로 들고 있는 것이 더 나았을 것 같다. 아무리 봐도.;
Posted by Y2K
,
indows에서 System의 모든 Monitoring은 WMI를 통해서 가능하다.
WMI를 이용하는 경우, Remote에서 접근이 용의하다는 장점과 Server군의 경우 다양한 속성들을 모두 Monitoring할 수 있다는 큰 장점을 가지고 있다. 또한, 기본적인 WMI의 Query를 통해서 값을 얻는 것이 아닌, .NET에서는 WMI에 대한 Event Handling이 가능하다. 

지정될 수 있는 Event는 http://msdn.microsoft.com/en-us/library/aa394583(VS.85).aspx을 참조.

모니터링 소스는 다음과 같다. 

ConnectionOptions connectionOptions = new ConnectionOptions()
     {
            Authentication = AuthenticationLevel.Default,
            Username = @"UserName",
            Password = @"Password",
            EnablePrivileges = true,
            Impersonation = ImpersonationLevel.Impersonate
      };
            
ManagementScope scope = new ManagementScope(@"\\hyperv\root\cimv2", connectionOptions);
scope.Connect();

//__InstanceCreationEvent : Wmi instance create event
//__InstanceModificationEvent : Wmi instance value modified event
//__InstanceDeletionEvent : Wmi instance delete event
//__InstanceOperationEvent : Wmi instance method call event

WqlEventQuery wQuery = new WqlEventQuery("Select * From __InstanceModificationEvent Within 1 " + "Where TargetInstance ISA 'Win32_PerfFormattedData_Tcpip_NetworkInterface'");

ManagementEventWatcher watcher = new ManagementEventWatcher(scope, wQuery);
watcher.EventArrived += new EventArrivedEventHandler(DisplayWatcherEvent);
watcher.Start();

Console.ReadLine();
            
watcher.Stop();

일반적인 Monitoring Handling과 동일하다. 특징이 나타나는 곳은 오히려 Event 처리기쪽이 더 특징이 나타난다.

private static void DisplayWatcherEvent(object sender, EventArrivedEventArgs e)
{
    foreach(var property in e.NewEvent.Properties)
    {
        if(property.Name == "TargetInstance")
        {
            ManagementBaseObject managementObject = property.Value as ManagementBaseObject;
            if (managementObject == null)
            {
                Console.WriteLine("Data convert is null");
            }
            else
            {
                var bytesPerSec = managementObject.Properties["BytesTotalPersec"].Value;
                var bytesReceivedPersec = managementObject.Properties["BytesReceivedPersec"].Value;
                var bytesSentPersec = managementObject.Properties["BytesSentPersec"].Value;
                string name = managementObject.Properties["Name"].Value.ToString();

                Console.WriteLine("{0}\t{1}\t{2}\t{3}", 
                    name.Substring(0, 4), bytesPerSec, bytesReceivedPersec, bytesSentPersec);
            }
        }
    }
}
특징으로 보이는 것이 EventArrivedEventArgs에서 Property의 Value형태로 WMI Instance의 값이 전달된다는 점이다. 그리고, Performance Counter의 경우에는 이전값, 현재값이 Property의 Name으로 전달되기 때문에 Diff. Monitoring에도 용의하다. 


Posted by Y2K
,

MS SQL Query Script

.NET Framework 2009. 7. 13. 15:37

[System.Reflection.Assembly]::LoadWithPartialName("System")
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[System.Reflection.Assembly]::LoadWithPartialName("System.Data")

$fileContexts = [System.IO.File]::ReadAllLines("c:\mailStore.txt")
$connection = New-Object System.Data.SqlClient.SqlConnection("==THIS IS CONNECTION STRING==");

foreach($fileContext in $fileContexts) {
    $samAccount = $fileContext.Replace("name: ", "").Replace("@mailpush.co.kr", "")
       
    $connection.Open()
    $command = New-Object System.Data.SqlClient.SqlCommand;
    $command.Connection = $connection
    $command.CommandText = "SELECT PhoneNumber, Email From Product WHERE LogonName = '" + $samAccount + "'" + " and ProductStateId = 1"
    $reader = $command.ExecuteReader()

    while($reader.Read())
    {
        $reader.GetSqlString(0).ToString() + "`t" + $reader.GetSqlString(1).ToString() >> c:\output1.txt
    }
    $reader.Close()
    $connection.Close()   
}
[System.Windows.Forms.MessageBox]::Show("Complete!")
notepad.exe "c:\output1.txt"


재미있는 것은 .NET쪽 코드랑 별 다를 것이 없는 코드를 내가 작성하고 있다는 것이다. -_-


Posted by Y2K
,
.NET 객체를 이용하는 PowerShell의 object들은 모두다 이용가능하지만, Powershell은 결정적으로 자신만의 .NET 객체를 만들지 못하는 단점을 가지고 있다.

그렇지만, 시스템 관리 및 MS에서 제공되는 대부분의 (SCVMM, Exchange, MS-SQL) 제품들은 Powershell에서 사용할 수 있는 방법 및 cmdlet을 제공하고 있고, 이를 이용하면 불편하게 시스템을 바닥까지 긁어가면서 개발을 하지 않아도 된다.

그리고, .NET 객체로 PowerShell을 이용해서 개발을 할 수 있는 방법을 역시 MS가 제공하고 있기 때문에.. PowerShell만 조금 알고, 약간의 개발을 한다면 손쉽게 PowerShell을 이용해서 시스템을 제어하는 프로그램을 만들어줄 수 도 있다.

PowerShell을 이용한 개발을 하기 위해서는 다음과 같은 절차가 이루어져야지 된다.

1. PowerShell Runspace를 정해준다.
2. 실행될 Script가 동작될 Pipeline을 만들어준다.
3. Command 생성
4. Pipeline을 통해서 Script를 invoke 시켜준다.


1. Runspace 생성
Runspace는 자신의 application이 동작할 system pool과 동일한 개념이다.
먼저 RunspaceConfiguration을 만들어준 후, 그 Configuration을 이용해서 Runspace를 만들어준다.

2. Pipeline 생성
Pipeline은 Powershell을 사용하면 쉽게 알 수 있는 하나의 cmdlet이 실행될 때, 그에 대한 output의 연결 통로이다. 예를 들어, 아래와 같은 script가 실행이 되었을때,
get-service에서 얻어지는 모든 객체를 ForEach-Object 구문으로 전달하게 되는 것이 get-service 명령어가 종료된 이후에 pipeline을 통해서 전달되게 된다. Pipeline의 생성 구문은 다음과 같다. IDispose Interface를 상속받고 있기 때문에 using을 사용하는 것이 code가 깔끔해진다.

3. Command 생성
이제 판을 벌인 상태이고, 여기에서 굿을 보던 떡을 팔던 할 차례이다. cmdlet을 Command로 만들어주고, 그 cmdlet의 parameter를 각각 넣어주면 된다. 여기에서 재미있는 점중 하나가.. 일반적으로 get-service "SMS" 라고 넣어주면 되었던 것들이 get-service -name "SMS" 라는 식으로 full name으로 들어가져야지 된다는 것이다. Name이 없이 넣는 방법은 못찾겠다. ;;

재미있는 것이 pipeline에 여러 command를 넣는 것이 가능하다는 것이다. 여러 command를 넣어서 여러 작업을 동시에 automation을 시킬때 편리할 듯 싶다.

4. Pipeline Invoke
Invoke를 통해서 실행을 하고 그 return 값을 받는데.. 이 받는 과정이 조금 재미있다. PowerShell은 .NET 을 이용한다. 따라서, 이 Invoke의 Return값도 .NET 객체 또는 Wmi Script의 결과가 나오게 되는데. .NET만이 나온다면 System.Object를 사용하면 될 것 같은데.. WMI Object 역시 지원하고 있기 때문에 PSObject라는 약간 생뚱 맞은 Object가 튀어나오게 된다. PSObject에서 ImmediateObject Property를 이용해서 .NET Object로 casting을 해줘서 .NET에서 사용해줘도 되고, 그냥 일반적으로 PSObject.ToString()을 호출해주면 PowerShell에서 나오는 결과와 동일한 결과를 나타나게된다.

casting을 시켜줄때가 가장 문제인것 같은데.;;


Posted by Y2K
,
.NET 3.0 Framework을 내놓으면서 LINQToSQL을 발표하면서 데이터와 모델간의 하나의 큰 전환점을 MS는 가지고 왔다. Java 진형의 Hybernate나 iBatis에 대항한 LINQToSQL이 바로 이것인데.. Visual Studio에서 특별한 코딩이 없이, 바로 Db의 Schema를 데이터의 Type으로 얻어오는 것이 가능하고, 그 Type의 생성 및 수정, 삭제가 Data와 같이 연동되어서 사용되어 질 수 있는 MS 진형에서는 나름 혁명적인 사건이라고도 할 수 있다.; (개인적으로는.;)

물리적 Model인 Database의 data를 개념적인 모델인 class로 변경시키고, 개발자들은 보다더 개발에 대해서 Application과 object에 대한 관점을 높이는 것이 보다 더 나은 application을 만들 수 있다는 것이 기본적인 발상인데.. 

이번에 발표된 내용으로는 Visual Studio 2008에 있던 LINQToSQL을 조금은 버리고, ADO .NET Entity Model을 중점으로 갈 것 같다는 느낌이 든다. 

무엇보다 MS SQL Server에 치중된 LINQToSQL이 아닌, ADO .NET 에서 얻을 수 있는 모든 데이터는 다 사용 가능한 ADO .NET Entity Model이 더욱더 좋은 모델이겠지만, 이거 얼마 안지나서 또 바뀌는 것에 대한 이 찜찜함은.;;;

기본적인 사용법은 ADO .NET Entity Model과 LINQToSQL이 거의 같다. 
차이가 있다면 DataContext로 연결된 데이터 모델이 ObjectContext로 변경되었고, Generate되던 코드들의 차이점정도 이외에는 큰 차이가 나타나지 않는다. 

큰 차이가 하나 있다. 기본적으로 LINQToSQL은 DataContext에서 Table에 Insert, Delete가 가능하지만, ADO .NET Entity Model에서는 ObjectContext에서 AddTo{Table}, DeleteObject method를 이용해서 Insert, Delete를 한다. 좀 더 접근성을 높인 명명법을 만든 것인지 모르겠지만.... 기존것이 더 직관적이긴 했는데.;

MS에서 발표하기로는 속도면에서는 LINQToSQL이 훨씬 낫다고 하는데..
이건 기본적으로 ADO .NET에서 SqlConnection, SqlCommand 자체가 MS Sql에서 훨씬 빨랐던 그 차이를 그대로 가지고 온 것에 불과하고.... 

그럼 나머지는 모두 DbConnection, DbCommand가 내부적으로 구현되어서 사용되고 있는 것인가??? ;;
한번 뒤져볼 내용일 것 같다. 
Posted by Y2K
,
log4net을 주로 사용하고 있지만, 이번에 Scott의 blog에서 소개한 ELMAH를 보고 감탄~ 

db를 이용한 log 기록 및 web page를 통해 log monitor, e-mail notification 까지 모두 가능하다. 

가장 보고 놀라버린 log monitor


MSSQL, MySql, SQLite, Oracle 을 모두 지원하며 각각에 대한 Filtering까지 모두 지원한다. 
에서 간단한 소개가 있고, web.config 설정은 다음과 같다. 

1. configsections에 elmah 등록
    <sectionGroup name="elmah">
      <section name="security" requirePermission="false" type="Elmah.SecuritySectionHandler, Elmah"/>
      <section name="errorLog" requirePermission="false" type="Elmah.ErrorLogSectionHandler, Elmah"/>
      <section name="errorMail" requirePermission="false" type="Elmah.ErrorMailSectionHandler, Elmah"/>
    </sectionGroup>
2. configuration에 elmah 세부 정보 등록
  <elmah>
    <errorLog type="Elmah.SQLiteErrorLog, Elmah" connectionStringName="ELMAH.SQLite" />
    <errorMail from="fromaddress" to="toaddress" smtpServer="smtpmails" subject="Error Mails" />
  </elmah>
3. httpmodule에 사용할 monitor page 이름과 errorMail 모듈 등록
      <add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah"/>
      <add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah"/>
4. httphandlers에 사용될 monitor page 등록
      <add verb="POST,GET,HEAD" path="traceError.axd" type="Elmah.ErrorLogPageFactory, Elmah" />
5. customerError mode를 on으로 변경
     <customErrors mode="On" defaultRedirect="/Home/Index">


다른 페이지에도 한번 적용해보고 싶은 내용. ^^

그리고 ASP .NET MVC 모델에 적용하기 위해서 가장 쉬운 방법은 ErrorHandlerAttribute를 상속받은 ELMAH 전용 ErrorHandler를 만들어주면 사용이 매우 편하다. 




Posted by Y2K
,
이번에 ASP .NET 사이트에서 발표된 Chart control의 경우에는 지금까지 있었던 상용 Chart들을 완전히 뒤집어버릴 수준의 control을 제시하고 있다. 일반 2D Chart에서 3D Chart까지 거의 안되는 Chart가 없을 정도로 많은 표현을 제공하고 있는데, 한 3년전까지만 해도 chart 만드는 것때문에 엄청나게 고생하던 것을 생각하면 참 여러가지로 많은 생각이 들게 된다. (그때 만든 Chart DLL을 생각하면 눈물이 앞을.. T_T)

그런데, 일반 ASP .NET에서는 chart control을 server 객체로 만들어서 사용을 하는데, MVC에서는 서버 Control이 사용되지 않기 때문에 다른 방법으로 사용해줘야지 된다. 내용을 조금 뒤져본 결과, Chart.SaveImage 함수를 이용해서 Temp 파일로 저장하는 방법으로 해결 가능한 것을 알았다. View에서 저장되는 FileName을 표시해주면 간단하게 해결. 

Chart chart = new Chart();
chart.BackColor = Color.White;
chart.Height = 300;
chart.Width = 450;
chart.AntiAliasing = AntiAliasingStyles.All;
chart.ImageType = ChartImageType.Png;

chart.Titles.Add(new Title() {Text = "판매량", ForeColor = Color.Black});
chart.Legends.Add(new Legend());

Series series = chart.Series.Add("data1");
series.ChartType = SeriesChartType.Column;
series.YValueType = ChartValueType.Int32;
series.LegendText = "Dates";
series.BorderColor = Color.Azure;
series.ShadowOffset = 2;
series["DrawingStyle"] = "Cylinder";

Random rand = new Random();
int dataSize = rand.Next(10, 20);
for(int i = 0 ; i < dataSize ; i++)
{
    string legendText = String.Format("DP {0}", i);
    Double dblValue = (double) rand.Next(1000);
    series.Points.Add(new DataPoint() { YValues = new Double[] { dblValue }, LegendText = legendText });
}

Series series2 = chart.Series.Add("data2");
series2.ChartType = SeriesChartType.Column;
series2.YValueType = ChartValueType.Int32;
series2.LegendText = "Dates2";
series2.BorderColor = Color.Azure;
series2.ShadowOffset = 2;
series2.Color = Color.RosyBrown;
for(int i = 0; i < dataSize; i++)
{
    string legendText = String.Format("DP {0}", i);
    Double dblValue = (double)rand.Next(1000);
    series2.Points.Add(new DataPoint() { YValues = new Double[] { dblValue }, LegendText = legendText });
}


ChartArea chartArea = chart.ChartAreas.Add("Default");
chartArea.BackColor = Color.Transparent;
chartArea.BorderColor = Color.Red;
chartArea.AxisX.IsMarginVisible = true;
chartArea.Area3DStyle.Enable3D = false;

MemoryStream ms = new MemoryStream();
chart.SaveImage(Server.MapPath("~/img.png")); //Save 파일 Name

ms.Dispose();
chartArea.Dispose();
series.Dispose();
chart.Dispose();




Posted by Y2K
,

Singletone Pattern

.NET Framework 2009. 3. 31. 03:04
예전에 Design Pattern에서 언급되던 Singletone pattern에서 다른 언어들과 .NET에서 구현상의 큰 차이점이 발견되어서 적어두기. 

1. 다른 언어들.
    public sealed class SingletonOld
    {
        private static Object lockObj = new Object();
        private static SingletonOld singleObject;

        private SingletonOld()
        {
        }

        public static SingletonOld Value
        {
            get 
            {
                if(singleObject == null)
                {
                    lock(lockObj)
                    {
                        singleObject = new SingletonOld();
                    }
                }
                return singleObject;
            }
        }
    }


2. .NET
    public sealed class SIngletonNew
    {
        private SIngletonNew()
        {

        }
        private static SIngletonNew singleObject = new SIngletonNew();

        public static SIngletonNew Value
        {
            get { return singleObject; }
        }
    }

가장 큰 차이는 변수의 선언과 동시에 값의 할당. CLR에서는 변수의 선언과 동시에 값을 할당시켜주는 경우에 생성자의 inline code로 만들어서 넣어주기 때문에 가장 빠르고 static의 경우에는 Single Thread의 접근을 완벽하게 보장해준다. 그러나 예전의 방법대로 구현하게 되면 Thread의 동기화 lock에 의해서 효율성을 떨어뜨리는 구현이 된다. (불행하게도 Design Pattern 책에는 대부분 첫번째 방법으로 되어있다.;; 두번째 방법 비슷하게 private 의 생성자 구현을 만들어두고 이렇게 만들면 안되고 첫번째 방법으로 Thread의 lock을 유지시켜야지 된다는 친절한 설명이 있는 책도 있다.;; )




Posted by Y2K
,
1. FCL과 C# 기본 형식 value type에서 FCL을 사용 
ex : 
 C# 기본 형식 > byte , FCL > System.Byte
-> 조금은 생뚱맞아보이는 방법이긴 하지만, 각 언어들에서 정확한 데이터 형식에 mapping 되는 것을 도와줄 수 있다. 특히 int 값에 대해서 Int32, Int64, Int16 과 같이 다양한 데이터 크기가 있을때, 이에 대한 형식 mapping에서 에러가 발생할 수 있다.
unmanaged code를 사용하는 경우, 이와 같은 일들은 빈번하게 발생 될 수 있기 때문에, 전 code를 FCL로 사용하는 것이 좋다. 

-> 참조 형식과 값 형식에 대해서 명확한 구분이 가능하다. string과 String의 경우에 같은 형식이지만 visual studio에서 색상 설정에 따라서 다르게 보인다. 이는 reference type과 value type을 명확히 구분이 가능하도록 visual studio에서 설정되어있기 때문이다. 그렇지만, C# 기본 형식으로 표기를 할 경우에는 value type인 int와 reference type인 string의 표기가 같게 된다. 

2. Value type과 reference type의 사용에 있어 주의 
-> 기본으로 돌아가서, value type은 stack, reference type은 heap에 생성된다. data의 memory 위치에 따라서 GC의 동작 및 GC 수집의 횟수에 영향을 미치게 된다. 

3. \r\n 을 사용하는 것보다는 Environment.NewLine을 사용하라. 
-> Mono를 비롯하여 CLR의 다른 OS로의 포팅이 계속해서 지원되고 있다. 
: 언제나 다 되려나... 싶긴 하다만.;


FCL 부분의 경우에는 많은 논란이 있을 것 같은 부분이다. 일반적으로 사람들이 사용하는 코드와는 많은 차이를 줄 수 있으니까. 그렇지만, 직접 사용해본 결과로는 보다 더 코드를 알아보기에 편한 느낌이 든다. 무엇보다 Value type과 Reference type간의 눈으로 보이는 차이는, 형식의 선언에서 한번 더 생각해보는 계기를 가지고 오는 것 같다. 
Posted by Y2K
,

CLR via C#

.NET Framework 2009. 3. 24. 01:10
.NET을 사용해서 개발한지 4년정도가 되어가는 때에 딱 적당하게 찾아서 보고 있는 책이라는 느낌이 든다. 
이 책은 기본적으로 .NET을 사용하고 있는 개발자를 대상으로 하고 있는 책으로 대부분의 책에서 나오는 Hello World와 같은 실행되는 코드는 전혀 나오지를 않는다. 

한 언어 또는 Framework를 다루는 책중에서 이정도까지 코드가 나오지 않는 책이 있었는지.. 라는 생각이 무척 많이 들게 하는 책으로 .NET Framework의 밑바닥에서 어떤 일들이 일어나는지.. method로 정의가 될때와 property로 정의될때 JIT의 compile이 어떤 식으로 구현이 되는지와 event의 등록 및 해재가 일어날때, CLR에서 어떻게 처리가 되고 있는지에 대한 그 전에는 단지 '사용하고 있었던' 코드에 대한 밑바닥 지식을 제공한다. 

method를 만들고, property를 만들때 각각 어떤 상황에서 어떤 방법을 사용하면 좋을지에 대한 고민을 한번 심각하게 하게 만들고, 내가 가진 coding 습관중에서 몇가지를 바꿔야지 될 것들이 보인다. 조금은 특이할지 모르겠지만 이 책에서 이야기 하는 논리에 상당히 설득을 당한 상태이기도 하고. 약간... 아니 많은 코딩 습관에 대해서 다시 한번 질문을 던져주는 책이 되는 것 같다. 

그리고, 가장 중요한 것. value와 reference에 대한 고민을 한번 더 나한테 던지고 있다. 어떤 상황에서 어떤 type을 사용하는 것이 가장 좋은가. 라는 고민이 계속해서 생기게 된다. 과연 내가 개발을 할 때, 적정한 상황에서 적정한 type을 이용하고 있는 것일까? 라는 고민과 좀더 나은 방법으로 개발할 수 있는 방법은 무엇일까? 라는 고민들이 계속해서 내 머리속을 스쳐지나가고 있다. 

어떻게 하면 좋은 프로그램을 만들수 있을까? 라는 끝없는 고민을 던져 주면서, .NET Framework의 개발 이론에서 이런 부분이 있어서 이렇게 되었구나.. 하는 깨달음을 주는 책. 정독으로 읽어도 읽어도 계속해서 놓친 점이 나오고 있다. 

지금까지 읽었던 .NET 책중에서 최고라고 칠 수 있고, 내가 다른 사람들에게 추천을 한다면 .NET Gotcha를 읽어본 사람이 읽어볼 책으로 추천하고 싶다. 이 책은 물고기 잡는 법을 가르쳐주지를 않는다. 이 책은 물고기에 대해서 가르쳐주는 책이다. 
Posted by Y2K
,