2011-08-18 2 views
21

C#에서 일부 기능적인 것을 사용하고 있습니다. List.Add이 업데이트 된 목록을 반환하지 않는다는 사실에 계속 머물러 있습니다.C의 쉼표 연산자와 동일한 관용구 C#이 있습니까?

일반적으로 개체에 함수를 호출하고 업데이트 된 개체를 반환하고 싶습니다.

((accum, data) => accum.Add(data), accum) 

내가 이렇게 내 자신의 "쉼표 연산자"를 쓸 수있다 : C#을 쉼표 연산자가 있다면

예를 들어이 좋은 것 작동하는 것처럼

static T comma(Action a, Func<T> result) { 
    a(); 
    return result(); 
} 

것 같습니다를하지만, 전화 사이트는 추악 할 것입니다. 첫 번째 예는 다음과 같습니다.

((accum, data) => comma(accum.Add(data),()=>accum)) 

충분한 예제! 나중에 다른 개발자가 코드 소스에서 코를 주름지게하지 않고 이것을 수행하는 가장 깨끗한 방법은 무엇입니까?

+0

에는 list.add 새로운 목록을 반환하지 않고 그냥 장소를 수정 하나는 그 flunt 스타일 조각을 작성하려고 할 때

이해력 구문의 진정한 장점은 명백하다. 이 의미에서, 그것은 기능적이지 않습니다. –

답변

1

C# 3.0의 코드 블록을 사용하여 자연스럽게 첫 번째 예제를 자연스럽게 수행 할 수 있습니다.

((accum, data) => { accum.Add(data); return accum; }) 
14

나는 이것을 Fluent으로 알고 있습니다.

확장 메서드

static List<T> MyAdd<T>(this List<T> list, T element) 
{ 
    list.Add(element); 
    return list; 
} 
+2

Fluent 정의를 포함하여 호의적으로 호명되었습니다. –

+0

그런데 왜 LINQ를 사용하지 않았습니까? –

+0

@KevinRoche : 아무도 linq를 사용하지 말 것을 제안합니다. 사실이 방법은 linq와 잘 통합되는 것처럼 보입니다. – recursive

2

에게 연장 방법을 사용하여에는 list.add의 유창함 예는 거의 틀림없이 가장 좋은 방법이지만, 완전성 '을 위해, 확실한 대안 잊지 마세요 : 래퍼 클래스를.

public class FList<T> : List<T> 
{ 
    public new FList<T> Add(T item) 
    { 
     base.Add(item); 
     return this; 
    } 

    public new FList<T> RemoveAt(int index) 
    { 
     base.RemoveAt(index); 
     return this; 
    } 

    // etc... 
} 

{ 
    var list = new FList<string>(); 
    list.Add("foo").Add("remove me").Add("bar").RemoveAt(1); 
} 
2

래퍼 메서드를 작성하지 않아도되는 래퍼 클래스 대답을 만드는 것이 흥미로울 것이라고 생각했습니다.

public class FList<T> : List<T> 
{ 
    public FList<T> Do(string method, params object[] args) 
    { 
     var methodInfo = GetType().GetMethod(method); 

     if (methodInfo == null) 
      throw new InvalidOperationException("I have no " + method + " method."); 

     if (methodInfo.ReturnType != typeof(void)) 
      throw new InvalidOperationException("I'm only meant for void methods."); 

     methodInfo.Invoke(this, args); 
     return this; 
    } 
} 

{ 
    var list = new FList<string>(); 

    list.Do("Add", "foo") 
     .Do("Add", "remove me") 
     .Do("Add", "bar") 
     .Do("RemoveAt", 1) 
     .Do("Insert", 1, "replacement"); 

    foreach (var item in list) 
     Console.WriteLine(item);  
} 

출력 :

foo 
replacement 
bar 

편집

할 수 있습니다 C#을 인덱스 특성을 이용하여 구문 아래로 슬림.

은 간단하게이 방법을 추가

public FList<T> this[string method, params object[] args] 
{ 
    get { return Do(method, args); } 
} 

을 그리고 전화가 지금과 같은 다음 바꿈은 물론, 선택 사양 인으로

list = list["Add", "foo"] 
      ["Add", "remove me"] 
      ["Add", "bar"] 
      ["RemoveAt", 1] 
      ["Insert", 1, "replacement"]; 

.

구문을 해킹하는 데 약간 재미가 있습니다.

+2

나는 이와 같은 색인 속성을 사용하지 않을 것입니다.동시에 놀랍고 가장 못생긴;) (2 가지 이유 : get {} 속성에서 객체 변경 및 마술 문자열 사용) – devio

+0

@devio, 나는 추악함에 대해 논쟁하지 않을 것입니다.), 그러나 이들은 마술 끈이 아닙니다. 매직 문자열은 특수한/독특한 결과를 생성하는 것입니다. 그러나 이와 같이 하드 코딩 된 리터럴을 사용하면 물론 메서드 이름 (기호 없음!) 및 유형에 대한 컴파일 타임 검사를 생략 할 수 있습니다. 그것은 C#을 다소 약한 타입의 동적 언어 (천천히 지옥은 말할 것도없이!)로 바꿔 놓습니다. 재미 :) –

+1

당신은 메서드 이름 (런타임에 문자열로 변환)에 대한 열거 형을 사용할 수 있습니다 - 그것은 어떤 techincal 차이를 만들지 않지만 그것은 깔끔하고 훨씬 오류가 발생하기 쉽습니다. (또는 const를 사용할 수도 있습니다) –

3

이것은 Concat http://msdn.microsoft.com/en-us/library/vstudio/bb302894%28v=vs.100%29.aspx의 용도입니다. 하나의 항목을 배열로 묶기 만하면됩니다. 기능 코드는 원본 데이터를 변경해서는 안됩니다. 성능이 문제가되고 이것이 충분하지 않다면 더 이상 기능 패러다임을 사용하지 않을 것입니다.

((accum, data) => accum.Concat(new[]{data})) 
+1

은 나에게 답을 줄 수있는 LINQ-y-est처럼 보였습니다. * shrug * – shelleybutterfly

2

나는이 스레드 아주 오래된 것을 알고,하지만 난 미래의 사용자에 대한 다음과 같은 정보를 추가 할 : 현재 이러한 연산자 없다

. 다음과 같이 번역 될 수

int square = (int x = int.Parse(Console.ReadLine()); Console.WriteLine(x - 2); x * x); 

:

int square = compiler_generated_Function(); 

[MethodImpl(MethodImplOptions.AggressiveInlining)] 
private int compiler_generated_Function() 
{ 
    int x = int.Parse(Console.ReadLine()); 

    Console.WriteLine(x - 2); 

    return x * x; 
} 

그러나이 기능은 최종 C#을 릴리스하기 전에 떨어 된 C# 6 개발주기 동안 semicolon operator 같이, 추가되었습니다.

+0

VS2015 Update 2에서이 샘플을 컴파일하여 프레임 워크 4.6.1을 대상으로하고 C# 6에 명시 적으로 언어 버전을 설정하지 못했습니다. Program.cs (9,27,9,30) : 오류 CS1525 : 잘못된 표현식 용어 'int' Program.cs (9,31,9,32) : 오류 CS1026 :) 예상 Program.cs (9,31,9,32) : 오류 CS1002 :; 예상 Program.cs (9,9,9,97) : 오류 CS1002 :; 예상 Program.cs (9,9,9,97) : 오류 CS1513 :} 예상 내가 잘못 했습니까? – ISanych

+2

이 기능은 C# 6에서 삭제되었습니다. 릴리스 버전에서는이 기능을 사용할 수 없었습니다. – Athari

0

또 다른 기술은 바로 다음과 같습니다.

/// <summary>TODO</summary> 
public struct IO<TSource> : IEquatable<IO<TSource>> { 
    /// <summary>Create a new instance of the class.</summary> 
    public IO(Func<TSource> functor) : this() { _functor = functor; } 

    /// <summary>Invokes the internal functor, returning the result.</summary> 
    public TSource Invoke() => (_functor | Default)(); 

    /// <summary>Returns true exactly when the contained functor is not null.</summary> 
    public bool HasValue => _functor != null; 

    X<Func<TSource>> _functor { get; } 

    static Func<TSource> Default => null; 
} 

을 그리고 이러한 확장 방법과 LINQ-수 모나드합니다 :이 같은 IO 구조체를 정의

[SuppressMessage("Microsoft.Naming", "CA1724:TypeNamesShouldNotMatchNamespaces")] 
public static class IO { 
    public static IO<TSource> ToIO<TSource>(this Func<TSource> source) { 
     source.ContractedNotNull(nameof(source)); 
     return new IO<TSource>(source); 
    } 

    public static IO<TResult> Select<TSource,TResult>(this IO<TSource> @this, 
     Func<TSource,TResult> projector 
    ) => 
     @this.HasValue && projector!=null 
      ? New(() => projector(@this.Invoke())) 
      : Null<TResult>(); 

    public static IO<TResult> SelectMany<TSource,TResult>(this IO<TSource> @this, 
     Func<TSource,IO<TResult>> selector 
    ) => 
     @this.HasValue && selector!=null 
      ? New(() => selector(@this.Invoke()).Invoke()) 
      : Null<TResult>(); 

    public static IO<TResult> SelectMany<TSource,T,TResult>(this IO<TSource> @this, 
     Func<TSource, IO<T>> selector, 
     Func<TSource,T,TResult> projector 
    ) => 
     @this.HasValue && selector!=null && projector!=null 
      ? New(() => { var s = @this.Invoke(); return projector(s, selector(s).Invoke()); }) 
      : Null<TResult>(); 

    public static IO<TResult> New<TResult> (Func<TResult> functor) => new IO<TResult>(functor); 

    private static IO<TResult> Null<TResult>() => new IO<TResult>(null); 
} 

지금 당신은 따라서 LINQ 포괄적 인 구문 사용할 수 있습니다

using Xunit; 
[Fact] 
public static void IOTest() { 
    bool isExecuted1 = false; 
    bool isExecuted2 = false; 
    bool isExecuted3 = false; 
    bool isExecuted4 = false; 
    IO<int> one = new IO<int>(() => { isExecuted1 = true; return 1; }); 
    IO<int> two = new IO<int>(() => { isExecuted2 = true; return 2; }); 
    Func<int, IO<int>> addOne = x => { isExecuted3 = true; return (x + 1).ToIO(); }; 
    Func<int, Func<int, IO<int>>> add = x => y => { isExecuted4 = true; return (x + y).ToIO(); }; 

    var query1 = (from x in one 
        from y in two 
        from z in addOne(y) 
        from _ in "abc".ToIO() 
        let addOne2 = add(x) 
        select addOne2(z) 
       ); 
    Assert.False(isExecuted1); // Laziness. 
    Assert.False(isExecuted2); // Laziness. 
    Assert.False(isExecuted3); // Laziness. 
    Assert.False(isExecuted4); // Laziness. 
    int lhs = 1 + 2 + 1; 
    int rhs = query1.Invoke().Invoke(); 
    Assert.Equal(lhs, rhs); // Execution. 

    Assert.True(isExecuted1); 
    Assert.True(isExecuted2); 
    Assert.True(isExecuted3); 
    Assert.True(isExecuted4); 
} 

만 작성하고 리턴하는 IO 모 드를 원할 때이 구조체를 정의하고 엔트 방법 :

from pass in Enumerable.Range(0, int.MaxValue) 
let counter = Readers.Counter(0) 
select (from state in gcdStartStates 
     where _predicate(pass, counter()) 
     select state) 
into enumerable 
where (from _ in Gcd.Run(enumerable.ToList()).ToIO() 
     from __ in ConsoleWrite(Prompt(mode)) 
     from c in ConsoleReadKey() 
     from ___ in ConsoleWriteLine() 
     select c.KeyChar.ToUpper() == 'Q' 
    ).Invoke() 
select 0; 

오래 C 콤마 연산자 용이 무엇인지 인식된다 : 용이 같은 코드 단편의 기입을 허용

public struct Unit : IEquatable<Unit>, IComparable<Unit> { 
    [CLSCompliant(false)] 
    public static Unit _ { get { return _this; } } static Unit _this = new Unit(); 
} 

public static IO<Unit> ConsoleWrite(object arg) => 
    ReturnIOUnit(() => Write(arg)); 

public static IO<Unit> ConsoleWriteLine(string value) => 
    ReturnIOUnit(() => WriteLine(value)); 

public static IO<ConsoleKeyInfo> ConsoleReadKey() => new IO<ConsoleKeyInfo>(() => ReadKey()); 

모나드 작성 작업.

(Enumerable.Range(0,int.MaxValue) 
      .Select(pass => new {pass, counter = Readers.Counter(0)}) 
      .Select(_ => gcdStartStates.Where(state => _predicate(_.pass,_.counter())) 
              .Select(state => state) 
        ) 
).Where(enumerable => 
    ((Gcd.Run(enumerable.ToList())).ToIO() 
     .SelectMany(_ => ConsoleWrite(Prompt(mode)),(_,__) => new {}) 
     .SelectMany(_ => ConsoleReadKey(),   (_, c) => new {c}) 
     .SelectMany(_ => ConsoleWriteLine(),  (_,__) => _.c.KeyChar.ToUpper() == 'Q') 
    ).Invoke() 
).Select(list => 0);