문제

이 두 가지 방법은 반복을 나타냅니다.

public static Expression<Func<Foo, FooEditDto>> EditDtoSelector()
{
    return f => new FooEditDto
    {
        PropertyA = f.PropertyA,
        PropertyB = f.PropertyB,
        PropertyC = f.PropertyC,
        PropertyD = f.PropertyD,
        PropertyE = f.PropertyE
    };
}

public static Expression<Func<Foo, FooListDto>> ListDtoSelector()
{
    return f => new FooDto
    {
        PropertyA = f.PropertyA,
        PropertyB = f.PropertyB,
        PropertyC = f.PropertyC
    };
}

이 반복을 제거하기 위해 어떻게 리팩터를 리팩터링 할 수 있습니까?

업데이트 : 죄송합니다. 중요한 요점을 언급하지 않았습니다. Fooeditdto는 Foodto의 서브 클래스입니다.

도움이 되었습니까?

해결책

만약에 FooEditDto 의복입니다 FooDto 그리고 당신은 memberinitexpressions가 필요하지 않으며 생성자를 사용하십시오.

class FooDto
 { public FooDto(Bar a, Bar b, Bar c) 
    { PropertyA = a;
      PropertyB = b;
      PropertyC = c;
    }
   public Bar PropertyA {get;set;}
   public Bar PropertyB {get;set;}
   public Bar PropertyC {get;set;}
 }

class FooEditDto : FooDto
 { public FooEditDto(Bar a, Bar b, Bar c) : base(a,b,c)
   public Bar PropertyD {get;set;}
   public Bar PropertyE {get;set;}
 }

public static Expression<Func<Foo, FooEditDto>> EditDtoSelector()
{
    return f => new FooEditDto(f.PropertyA,f.PropertyB,f.PropertyC)
    {
        PropertyD = f.PropertyD,
        PropertyE = f.PropertyE
    };
}

다른 팁

글쎄, 나는있다 정말 끔찍합니다 당신이 할 수있는 방법.

반사 (나와 함께 베어!)를 사용하여 특정 유형의 모든 속성을 해결하고 해당 유형에서 다른 유형에서 다른 속성을 복사하기 위해 대의원 (반사)을 구축하는 방법을 작성할 수 있습니다. 그런 다음 익명 유형을 사용하여 복사 대의원을 한 번만 빌드하면되므로 빠릅니다. 그런 다음 방법은 다음과 같습니다.

public static Expression<Func<Foo, FooEditDto>> EditDtoSelector()
{
    return f => MagicCopier<FooEditDto>.Copy(new { 
        f.PropertyA, f.PropertyB, f.PropertyC, f.PropertyD, f.PropertyC
    });
}

여기에 뉘앙스 :

  • MagicCopier는 일반적인 유형이며 사본은 일반적인 방법으로 "대상"유형을 명시 적으로 지정할 수 있지만 "소스"유형을 암시 적으로 지정합니다.
  • 프로젝션 이니셜 라이저를 사용하여 익명 유형을 초기화하는 데 사용되는 표현식에서 속성의 이름을 추론합니다.

나는 그것이 정말로 그만한 가치가 있는지 확실하지 않지만 아주 재미있는 아이디어입니다 ... 어쨌든 그것을 구현해야 할 수도 있습니다 :)

편집 : MemberInitexpression 우리는 표현 트리로 모든 것을 할 수있어 코드보다 훨씬 쉽게 만듭니다. 오늘 밤 시도해 볼 것입니다 ...

편집 : 완료되면 실제로 매우 간단한 코드입니다. 수업은 다음과 같습니다.

/// <summary>
/// Generic class which copies to its target type from a source
/// type specified in the Copy method. The types are specified
/// separately to take advantage of type inference on generic
/// method arguments.
/// </summary>
public static class PropertyCopy<TTarget> where TTarget : class, new()
{
    /// <summary>
    /// Copies all readable properties from the source to a new instance
    /// of TTarget.
    /// </summary>
    public static TTarget CopyFrom<TSource>(TSource source) where TSource : class
    {
        return PropertyCopier<TSource>.Copy(source);
    }

    /// <summary>
    /// Static class to efficiently store the compiled delegate which can
    /// do the copying. We need a bit of work to ensure that exceptions are
    /// appropriately propagated, as the exception is generated at type initialization
    /// time, but we wish it to be thrown as an ArgumentException.
    /// </summary>
    private static class PropertyCopier<TSource> where TSource : class
    {
        private static readonly Func<TSource, TTarget> copier;
        private static readonly Exception initializationException;

        internal static TTarget Copy(TSource source)
        {
            if (initializationException != null)
            {
                throw initializationException;
            }
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }
            return copier(source);
        }

        static PropertyCopier()
        {
            try
            {
                copier = BuildCopier();
                initializationException = null;
            }
            catch (Exception e)
            {
                copier = null;
                initializationException = e;
            }
        }

        private static Func<TSource, TTarget> BuildCopier()
        {
            ParameterExpression sourceParameter = Expression.Parameter(typeof(TSource), "source");
            var bindings = new List<MemberBinding>();
            foreach (PropertyInfo sourceProperty in typeof(TSource).GetProperties())
            {
                if (!sourceProperty.CanRead)
                {
                    continue;
                }
                PropertyInfo targetProperty = typeof(TTarget).GetProperty(sourceProperty.Name);
                if (targetProperty == null)
                {
                    throw new ArgumentException("Property " + sourceProperty.Name 
                        + " is not present and accessible in " + typeof(TTarget).FullName);
                }
                if (!targetProperty.CanWrite)
                {
                    throw new ArgumentException("Property " + sourceProperty.Name 
                        + " is not writable in " + typeof(TTarget).FullName);
                }
                if (!targetProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType))
                {
                    throw new ArgumentException("Property " + sourceProperty.Name
                        + " has an incompatible type in " + typeof(TTarget).FullName);
                }
                bindings.Add(Expression.Bind(targetProperty, Expression.Property(sourceParameter, sourceProperty)));
            }
            Expression initializer = Expression.MemberInit(Expression.New(typeof(TTarget)), bindings);
            return Expression.Lambda<Func<TSource,TTarget>>(initializer, sourceParameter).Compile();
        }
    }

그리고 그것을 부르기 :

TargetType target = PropertyCopy<TargetType>.CopyFrom(new { First="Foo", Second="Bar" });

반복은 이름에 있지만 C#은 한 클래스의 Propertya가 다른 클래스의 Propertya와 연결되어 있다는 것을 전혀 모릅니다. 연결을 명시 적으로 만들어야합니다. 당신이 한 방식은 잘 작동합니다. 충분한 경우 반사를 사용하여 모든 클래스에 대해이를 수행 할 수있는 한 가지 방법을 작성하는 것이 좋습니다.

선택한 방법의 성능 영향에주의를 기울이십시오. 반사 자체가 느리게 진행됩니다. 그러나 반사를 사용하여 일단 방출되면 글을 쓴 것만 큼 빨리 실행할 수 있습니다. 표현식 트리를 생성하고 컴파일 된 대표로 변환 할 수도 있습니다. 이러한 기술은 다소 복잡하므로 트레이드 오프를 평가해야합니다.

발신자가 필요한 속성만으로 익명 유형의 자신의 객체를 반환 할 수 있습니다.

public static Expression<Func<Foo,T>> 
                             GetSelector<T>(Expression<Func<Foo,T>> f)
 { return f;
 }

/* ... */
var expr = GetSelector(f => new{f.PropertyA,f.PropertyB,f.PropertyC});
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top