아래 코드를 linqpad로 실행하면

void Main()

{

           Test t = new Test();

    var y = t.aaa();

   

           System.Console.WriteLine(y);

}

 

class Test {

    public int aaa() {

        int x = 1;

 

        try {

            return (x += 1);

        } catch (Exception e) {

 

        } finally {

            x += 3;

        }

        return x;

    }

 }

 

출력은 2가 된다.

 

위 코드의 출처는 http://www.pixelstech.net/article/1474892842-try-%7B-return-%7D-finally-%7B%7D 이다

언어는 java를 대상으로 했지만 c#도 비슷한 것 같다.

 


출력이 2가 나오는 이유는 (java의 경우) 다음과 같다JVM 문서를 보면


만약 try 절에서 return을 실행할 경우 컴파일 된 코드는 다음과 같은 처리를 한다.

- 로컬 변수에 반환 값을(만일 존재하는 경우)을 저장한다.

- finally절 코드까지 jsr를 실행한다.

- finally절의 return에서 로컬 변수에 저장된 값을 반환한다.

 

return ++x가 실행되면 JVM++x의 값을 임시 변수에 저장하고, finally 블록을 계속 실행한다. finally가 실행된 뒤 임시 변수에 저장되어 있는 값을 메서드의 호출자에게 돌려준다.

 

저작자 표시
신고
by 흥배 2016.11.18 08:00

잘못된 SqlConnection

// 코드 1

using (var connection = new SqlConnection("접속문자열"))

{

    connection.Open();

    Parallel.For(1, 1000, x =>

    {

        var _ = connection.Query<DateTime>("select current_timestamp").First(); // Dapper

    });

}


<코드 1>을 실행하면 크래쉬가 발생

이유는 간단 SqlConnection가 스레드 세이프 하지 않기 때문.


위의 코드를 올바르게 수정하려면 아래와 같이 변경.

// 코드 2

Parallel.For(1, 1000, x =>

{

    using (var connection = new SqlConnection("접속문자열"))

    {

        connection.Open();

        var _ = connection.Query<DateTime>("select current_timestamp").First(); // Dapper

    }

});



<코드 2>는 안전하지만 아마 위 방식은 원하는 방식이 아닐 것이다.



ThreadLocal

위의 문제를 ThreadLocal을 사용하여 해결해 보자


// 코드 3

using (var connection = new ThreadLocal<SqlConnection>(() => { var conn = new SqlConnection("접속문자열"); conn.Open(); return conn; }))

{

    Parallel.For(1, 1000, x =>

    {

        var _ = connection.Value.Query<DateTime>("select current_timestamp").First(); // Dapper

    });

}



성능 테스트를 해보면 싱글 스레드를 사용하면 16초, <코드 2>는 5초, <코드 3>은 2초



<코드 3>은 안전한가?

<코드 3>은 위험한 코드.

이유는 연결한 후 Dispose 하지 않기 때문.

ThreadLocal의 Dispose는 어디까지는 ThreadLocal의 Dispose이기 때문에 그 안의 객체를 Dispose 해주지 않는다.


이 문제는 trackAllValues 라는 옵션을 사용하여 간단하게 해결 가능!



// 코드 4

using (var connection = new ThreadLocal<SqlConnection>(() => { var conn = new SqlConnection("접속문자열"); conn.Open(); return conn; }

    , trackAllValues: true)) // ThreadLocalの.Values 프로퍼티의 참조를 유효화 한다

{

    Parallel.For(1, 1000, x =>

    {

        var _ = connection.Value.Query<DateTime>("select current_timestamp").First(); // Dapper

    });


    // 생성된 모든 Connection을 일괄적으로 Dispose

    foreach (var item in connection.Values.OfType<IDisposable>()) item.Dispose();

}



단 trackAllValues는 닷넷프레임워크 4.5 부터 사용 가능




trackAllValues 랩퍼 클래스 사용


public static class DisposableThreadLocal

{

    public static DisposableThreadLocal<T> Create<T>(Func<T> valueFactory)

        where T : IDisposable

    {

        return new DisposableThreadLocal<T>(valueFactory);

    }

}


public class DisposableThreadLocal<T> : ThreadLocal<T>

    where T : IDisposable

{

    public DisposableThreadLocal(Func<T> valueFactory)

        : base(valueFactory, trackAllValues: true)

    {

    }


    protected override void Dispose(bool disposing)

    {

        var exceptions = new List<Exception>();


        foreach (var item in this.Values.OfType<IDisposable>())

        {

            try

            {

                item.Dispose();

            }

            catch (Exception e)

            {

                exceptions.Add(e);

            }

        }


        base.Dispose(disposing);


        if (exceptions.Any()) throw new AggregateException(exceptions);

    }

}



// 사용

using (var connection = DisposableThreadLocal.Create(() => { var conn = new SqlConnection("접속문자열"); conn.Open(); return conn; }))

{

    Parallel.For(1, 1000, x =>

    {

        var _ = connection.Value.Query<DateTime>("select current_timestamp").First(); // Dapper

    });

}





출처: http://neue.cc/2013/03/09_400.html 

저작자 표시
신고
by 흥배 2016.11.04 08:00

C++로된 프로그램과 C#으로 만든 프로그램이 서로 패킷을 주고 받는 경우 외부 라이브러리를 사용하거나 C# 기본 기능을 사용한다.
외부 라이브러리로  CGDBuffer, protobuf, msgpack 등을 사용하거나 아니면 아래와 같이 직접 한다.

 

 

(C++)
// 패킷 헤더
struct PktHeader
{
 short PacketID;
 short BodySize;
};

 

// 로그인 패킷
struct PktReqLogin
{
 char UserID[17];
 char UserPW[17]
};

 

struct PktResLogin
{
 short ErrorCode;
};

 

 


[ C++ 서버로 패킷 보낼 때 ]
short packetID, string userID, string userPW;

 

var asciiUserID = System.Text.Encoding.Default.GetBytes(userID); // 멀티바이트 문자열. 유니코드 문자열은  System.Text.Encoding.Unicode.GetBytes
var dataUserID = new byte[16+1];
Buffer.BlockCopy(asciiUserID, 0, dataUserID, 0, asciiUserID.Length);

 

var asciiUserPW = System.Text.Encoding.Default.GetBytes(userPW);
var dataUserPW = new byte[16 + 1];
Buffer.BlockCopy(asciiUserPW, 0, dataUserPW, 0, asciiUserPW.Length);

 

List<byte> dataSource = new List<byte>();
dataSource.AddRange(BitConverter.GetBytes(packetID)); // 패킷 ID
dataSource.AddRange(BitConverter.GetBytes((Int16)(dataUserID.Length + dataUserPW.Length))); // 패킷 데이터(body)의 크기
dataSource.AddRange(dataUserID);
dataSource.AddRange(dataUserPW);

 

var send_data = dataSource.ToArray() // byte[]로 변환

 

 

[ C++ 서버에서 패킷 받았을 때 ]
var recvData = Network.Receive(); // receive로 데이터 받음

 

var packet = new PacketData();
packet.PacketID = BitConverter.ToInt16(recvData, 0);
packet.DataSize = BitConverter.ToInt16(recvData, 2);
packet.BodyData = new byte[packet.DataSize];

 

var errorCode = BitConverter.ToInt16(packet.BodyData, 0);
      

 

struct PacketData
{
 public Int16 PacketID;
 public Int16 DataSize;
 public byte[] BodyData;
}

 

 

 

이것 이외에도 방식은 있으니 구글링도 해보기 바란다

 

저작자 표시
신고
by 흥배 2016.09.05 08:00

C#은 배열의 오버런을 방지하기 위해 자동으로 for 문으로 배열을 순회할 때 자동으로 경계 조사를 한다.

문제는 배열의 크기를 확실하게 알고 배열 경계를 넘지 않도록 프로그래머 주의를 해도 컴파일러가 경계 조사 코드를 만들어 버려서 성능 측면에서 손해를 볼 수 있다.

 

배열의 경계 조사를 하지 않고 싶다면 for 문에서 조건 검사를 배열의 Length 멤버를 사용한다

 

static void Test_SimpleAscend(int[] a) {

           for (int i = 0; i < a.Length; i++)

                     a[i] = i;  // We get this.

}

 



좀 더 자세한 내용은 아래 링크를 참조

https://blogs.msdn.microsoft.com/clrcodegeneration/2009/08/13/array-bounds-check-elimination-in-the-clr/

 

저작자 표시
신고
by 흥배 2016.08.08 08:00

현재 내용으로 봐서는 2017 2분기쯤 되면 꽤 쓸만하겠네요^^;

링크: https://blogs.msdn.microsoft.com/dotnet/2016/07/15/net-core-roadmap/

 

 

8월말쯤에 1.0.1 나올 예정.

 

올해 말 혹은 내년 3월까지의 예정.

 

 

.NET Core Tooling

- .csproj/MSBuild project system 지원

- 프레임워크 관리용 명령어 추가

 

 

언어 관련. C# 7, VB 15 추가 언어 사양

- tuple, 패턴 매칭

- Value Task, Ref return, Throw expression, Binary Literals, Digit Separators

- Out vars, Local Function

 

 

ASP.NET

- Web Socket

- URL Rewriting Middleware

- Azure 관련 강화

- Service Fabric support via WebListener based server

- MVC & DI Startup Time Improvements

- 프리뷰

- SignalR

- View Pages (Views without MVC Controllers)

 

 

.NET Core Runtime

- ARM 32/64

- .NET Core Runtime

 

 

Entity Framework Core

- Azure

- Transient fault handling (resiliency)

- Mapping

- Custom type conversions

- Complex types (value objects)

- Entity entry APIs

- Update pipeline

- CUD stored procedures

- Better batching (TVPs)

- Ambient transactions

- Query

- Stability, performance.

- Migrations

- Seed data

- Stability

- Reverse engineer

- Pluralization

- VS item template (UX)

 

 

Q1 2017 / Q2 2017

- .NET Standard 2.0

 

 

출처: https://opcdiary.net/?p=32475

 

저작자 표시
신고
by 흥배 2016.07.26 10:05

C#은 공용체를 지원하지 않지만 StructLayout(LayoutKind.Explicit) 과 FieldOffset을 사용해서 공용체를 구현할 수 있다.

 

using System;
using System.Runtime.InteropServices;

public class UnionTest
{
  public static void Main()
  {
     var bytes = new TBytes();
     bytes.Word = 0x12345678;
     Console.WriteLine("{0:x}", bytes.Byte0);  // 78
     Console.WriteLine("{0:x}", bytes.Byte1);  // 56
     Console.WriteLine("{0:x}", bytes.Byte2);  // 34
     Console.WriteLine("{0:x}", bytes.Byte3);  // 12
  }
}

[StructLayout(LayoutKind.Explicit)]
public struct TBytes
{
  [FieldOffset(0)]
  public byte Byte0;

  [FieldOffset(1)]
  public byte Byte1;

  [FieldOffset(2)]
  public byte Byte2;

  [FieldOffset(3)]
  public byte Byte3;

  [FieldOffset(0)]
  public uint Word;
}

 

출처: http://qiita.com/tadnakam/items/a47421c120b5f30954fb

저작자 표시
신고
by 흥배 2016.02.15 08:00

class aaa {
        static void bbb (int a ,params string[] strs) {
        }
    }

    static void Main () {
        int a = 1;
        int b = 2;
        bbb(a,"foo","bar","baz");
        bbb(b);
    }
}

 

 

출처: http://qiita.com/satotin/items/81e1e9c41181c9766de6

저작자 표시
신고
by 흥배 2016.02.11 08:00

linq, ArrayCopy, BlockCopy, marshal 중 BlockCopy가 제일 빠름


테스트 코드

const int COUNT = 1000000;

static void Main(string[] args)

{

var basearray = Enumerable.Range(0, 1024).Select(x => (byte) x).ToArray();


var sw = Stopwatch.StartNew();

for (var i = 0; i < COUNT; i++)

{

var buffer = basearray.Skip(20).Take(20).ToArray();

}

sw.Stop();


Console.WriteLine($"linq {sw.ElapsedMilliseconds}ms");


sw = Stopwatch.StartNew();

for (var i = 0; i < COUNT; i++)

{

var buffer = new byte[20];

Array.Copy(basearray, 20, buffer, 0, 20);

}

sw.Stop();


Console.WriteLine($"ArrayCopy {sw.ElapsedMilliseconds}ms");


sw = Stopwatch.StartNew();

for (var i = 0; i < COUNT; i++)

{

var buffer = new byte[20];

Buffer.BlockCopy(basearray, 20, buffer, 0, 20);

}

sw.Stop();


Console.WriteLine($"BlockCopy {sw.ElapsedMilliseconds}ms");



sw = Stopwatch.StartNew();

var ptr = Marshal.AllocCoTaskMem(basearray.Length);

Marshal.Copy(basearray, 0, ptr, basearray.Length);

try

{


for (var i = 0; i < COUNT; i++)

{

var buffer = new byte[20];

Marshal.Copy(ptr + 20, buffer, 0, 20);

}

}

finally

{

Marshal.FreeCoTaskMem(ptr);

}

sw.Stop();


Console.WriteLine($"marshal {sw.ElapsedMilliseconds}ms");


Console.ReadKey();

}


결과

linq          1622ms

ArrayCopy   71ms

BlockCopy  55ms

marshal      62ms



출처: http://qiita.com/yu_ka1984/items/c97e0950486e3ecc5642 

저작자 표시
신고
by 흥배 2016.02.05 08:12

성능을 중요시 하는 프로그램을 만들 때(특히 Unity를 사용한 게임) GC를 조심해야 하므로 불필요한 new를 사용하지 않아야 한다.

그러니 일부 눈에 보이는 코드에는 new를 사용하지 않지만 내부에서 사용되는 경우가 있다.

 

1. 박싱화

var x = (object)10;

 

void Test(object o)

{

}

 

Test(10);

 

 

2. 제너릭을 사용할 때 Object를 사용하는 메소드 사용

bool IsSame<T>(T t1, T t2)

{

    return t1.Equals(t2);

}

위의 IsSame은 제너릭 함수라서 박싱화가 일어나지 않을 것이라고 생각하지만 Equals 메소드가 Object를 사용해서 박싱화가 일어난다.

public virtual bool Equals(Object obj);

 

이 문제를 회피하기 위해서는 아래처럼 사용해야 한다.

public interface IEquatable<T>

{

    bool Equals(T other);

}

 

bool IsSame<T>(T t1, T t2) where T : IEquatable<T>

{

    return t1.Equals(t2);

}

 

혹은

bool IsSame<T>(T t1, T t2)

{

    return EqualityComparer<T>.Default.Equals(t1, t2);

}

 

 

3. 람다식의 보이지 않는 new

static int DoubleStatic(int x)

{

    return x * 2;

}

 

int DoubleInstance(int x)

{

    return x * 2;

}

 

void Run()

{

    var two = int.Parse("2");

 

    Enumerable.Range(1, 1).Select(DoubleStatic);           // 1

    Enumerable.Range(1, 2).Select(DoubleInstance);         // 2

    Enumerable.Range(1, 3).Select(x => x * 2);             // 3

    Enumerable.Range(1, 4).Select(x => x * two);           // 4

    Enumerable.Range(1, 5).Select(x => DoubleStatic(x));   // 5

    Enumerable.Range(1, 6).Select(x => DoubleInstance(x)); // 6

}

 

패턴 1, 2는 메소드를 직접 넣은 경우로 델리게이트를 사용하므로 new를 사용한다.

패턴 3은 일반적으로 자주 사용하는 패턴으로 new를 사용하지 않는다.

패턴 4도 자주 사용되는 패턴으로 로컬 변수를 사용해서 클래스가 만들어지므로 new를 사용한다.

패턴 5static 메소드를 사용해서 첫 호출 이외에는 캐시된 것을 사용하므로 별 문제 되지 않는다.

패턴 6은 델리게이트를 사용하므로 new를 사용한다.

 

위의 코드의 안 보이는 코드를 표현하면 아래와 비슷해진다.

static Func<int, int> cacheA;

static Func<int, int> cacheB;

 

internal static int LambdaA(int x)

{

           return x * 2;

}

 

class Closure

{

    internal int two;

 

    internal int LambdaB(int x)

    {

        return x * two;

    }

}

 

internal static int LambdaC(int x)

{

           return DoubleStatic(x);

}

 

internal static int LambdaD(int x)

{

           return DoubleInstance(x);

}

 

void Run()

{

    var two = int.Parse("2");

 

    // 1 - Select(DoubleStatic)

    Enumerable.Range(1, 1).Select(new Func<int, int>(DoubleStatic));

 

    // 2 - Select(DoubleInstance)

    Enumerable.Range(1, 2).Select(new Func<int, int>(DoubleInstance));

 

    // 3 - Select(x => x * 2)

    if(cacheA != null)

    {

        cacheA = new Func<int, int>(LambdaA);

    }

    Enumerable.Range(1, 3).Select(cacheA);

 

    // 4 - Select(x => x * two)

    var closure = new Closure();

    closure.two = two;

    Enumerable.Range(1, 4).Select(new Func<int, int>(closure.LambdaB));

 

    // 5 - Select(x => DoubleStatic(x))

    if(cacheB != null)

    {

        cacheB = new Func<int, int>(LambdaC);

    }

    Enumerable.Range(1, 5).Select(cacheB);

 

    // 6 - Select(x => DoubleInstance(x))

    Enumerable.Range(1, 6).Select(new Func<int, int>(LambdaD));

}

 

 

 

출처: http://neue.cc/2016/01/06_525.html

 

저작자 표시
신고
by 흥배 2016.02.03 07:52

AccessViolationException 예외는 네이티브 쪽에서 메모리 접근 위반을 했을 때 발생 하는 예외 이다.

할당되지 않은 메모리 또는 코드가 접근 권을 갖지 않는 메모리를 읽거나 쓰기를 시도하면 네이티브 코드(언 세이프 코드)에서 액세스 위반이 발생한다.

 

그러나 보통 System.Exception의 파생 클래스인 셈이라서 catch에서 이 예외를 포착할 것으로 생각하지만 애플리케이션은 이 예외를 잡지 못해서 이상 종료를 한다.

 

.NET Framework 3.5 이전에는 AccessViolationException catch 할 수 있었지만 .NET Framework 4 이후에서는 못하도록 바뀌었다.

 

이 예외를 잡고 싶다면 두 가지 방법을 하나를 하면 된다.

1. HandleProcessCorruptedStateExceptionsAttribute

AccessViolationException catch 하고 싶은 메소드에HandleProcessCorruptedStateExceptionsAttribute를 붙여서 잡을 수 있다.

[HandleProcessCorruptedStateExceptions]

void DoSomething()

{

try {

AccessViolationException를 발생하는();

} catch(AccessViolationException e) {

//---포착 가능

}

}

 

 

2.legacyCorruptedStateExceptionsPolicy

하위 호환성 때문에 기존(.NET Framework 3.5 이전)처럼 애플리케이션 전체에서 포착할 수 있도록 하고 싶은 경우. 구성 파일(*. config)<legacyCorruptedStateExceptionsPolicy>요소를 넣어서 해결할 수 있다.

<configuration>

<runtime>

<legacyCorruptedStateExceptionsPolicy enabled="true"/>

</runtime>

</configuration>

 

 

출처: http://blog.xin9le.net/entry/2015/07/22/053738

 

 

저작자 표시
신고
by 흥배 2016.01.22 08:00
| 1 2 3 4 ··· 7 |