다음을 수행하려고합니다.
GetString(
inputString,
ref Client.WorkPhone)
private void GetString(string inValue, ref string outValue)
{
if (!string.IsNullOrEmpty(inValue))
{
outValue = inValue;
}
}
이것은 나에게 컴파일 오류를주고있다. 내가 달성하려고하는 것이 분명하다고 생각합니다. 기본적으로 GetString
입력 문자열의 내용을의 WorkPhone
속성으로 복사하고 싶습니다 Client
.
참조로 속성을 전달할 수 있습니까?
답변
속성은 참조로 전달할 수 없습니다. 이 제한을 해결할 수있는 몇 가지 방법이 있습니다.
1. 반환 값
string GetString(string input, string output)
{
if (!string.IsNullOrEmpty(input))
{
return input;
}
return output;
}
void Main()
{
var person = new Person();
person.Name = GetString("test", person.Name);
Debug.Assert(person.Name == "test");
}
2. 위임
void GetString(string input, Action<string> setOutput)
{
if (!string.IsNullOrEmpty(input))
{
setOutput(input);
}
}
void Main()
{
var person = new Person();
GetString("test", value => person.Name = value);
Debug.Assert(person.Name == "test");
}
3. LINQ 표현
void GetString<T>(string input, T target, Expression<Func<T, string>> outExpr)
{
if (!string.IsNullOrEmpty(input))
{
var expr = (MemberExpression) outExpr.Body;
var prop = (PropertyInfo) expr.Member;
prop.SetValue(target, input, null);
}
}
void Main()
{
var person = new Person();
GetString("test", person, x => x.Name);
Debug.Assert(person.Name == "test");
}
4. 반사
void GetString(string input, object target, string propertyName)
{
if (!string.IsNullOrEmpty(input))
{
var prop = target.GetType().GetProperty(propertyName);
prop.SetValue(target, input);
}
}
void Main()
{
var person = new Person();
GetString("test", person, nameof(Person.Name));
Debug.Assert(person.Name == "test");
}
답변
속성을 복제하지 않고
void Main()
{
var client = new Client();
NullSafeSet("test", s => client.Name = s);
Debug.Assert(person.Name == "test");
NullSafeSet("", s => client.Name = s);
Debug.Assert(person.Name == "test");
NullSafeSet(null, s => client.Name = s);
Debug.Assert(person.Name == "test");
}
void NullSafeSet(string value, Action<string> setter)
{
if (!string.IsNullOrEmpty(value))
{
setter(value);
}
}
답변
ExpressionTree 변형과 c # 7을 사용하여 래퍼를 작성했습니다 (누군가 관심이있는 경우).
public class Accessor<T>
{
private Action<T> Setter;
private Func<T> Getter;
public Accessor(Expression<Func<T>> expr)
{
var memberExpression = (MemberExpression)expr.Body;
var instanceExpression = memberExpression.Expression;
var parameter = Expression.Parameter(typeof(T));
if (memberExpression.Member is PropertyInfo propertyInfo)
{
Setter = Expression.Lambda<Action<T>>(Expression.Call(instanceExpression, propertyInfo.GetSetMethod(), parameter), parameter).Compile();
Getter = Expression.Lambda<Func<T>>(Expression.Call(instanceExpression, propertyInfo.GetGetMethod())).Compile();
}
else if (memberExpression.Member is FieldInfo fieldInfo)
{
Setter = Expression.Lambda<Action<T>>(Expression.Assign(memberExpression, parameter), parameter).Compile();
Getter = Expression.Lambda<Func<T>>(Expression.Field(instanceExpression,fieldInfo)).Compile();
}
}
public void Set(T value) => Setter(value);
public T Get() => Getter();
}
그리고 그것을 다음과 같이 사용하십시오 :
var accessor = new Accessor<string>(() => myClient.WorkPhone);
accessor.Set("12345");
Assert.Equal(accessor.Get(), "12345");
답변
속성을 가져오고 설정하려면 C # 7에서 이것을 사용할 수 있습니다.
GetString(
inputString,
(() => client.WorkPhone, x => client.WorkPhone = x))
void GetString(string inValue, (Func<string> get, Action<string> set) outValue)
{
if (!string.IsNullOrEmpty(outValue))
{
outValue.set(inValue);
}
}
답변
아직 언급되지 않은 또 다른 트릭은 속성 (예 : Foo
type Bar
) 을 구현하는 클래스 가 대리자를 정의 하고 내부 표현을 전달 delegate void ActByRef<T1,T2>(ref T1 p1, ref T2 p2);
하는 메소드 ActOnFoo<TX1>(ref Bar it, ActByRef<Bar,TX1> proc, ref TX1 extraParam1)
(및 가능하면 2 및 3 개의 “추가 매개 변수”의 버전)를 구현하는 것입니다 Foo
. 제공된 프로 시저를 ref
매개 변수로 사용합니다. 이 속성을 사용하는 다른 방법에 비해 몇 가지 큰 장점이 있습니다.
- 속성이 “제자리에”업데이트됩니다. 속성이 ‘Interlocked’메서드와 호환되는 유형이거나 그러한 유형의 노출 된 필드가있는 구조체 인 경우 ‘Interlocked’메서드를 사용하여 속성에 대한 원자 업데이트를 수행 할 수 있습니다.
- 특성이 노출 된 필드 구조 인 경우 구조의 필드는 중복 사본을 만들지 않고도 수정할 수 있습니다.
- `ActByRef` 메소드가 호출자에서 제공된 대리자로 하나 이상의 ‘ref’매개 변수를 전달하는 경우 싱글 톤 또는 정적 대리자를 사용할 수 있으므로 런타임시 클로저 또는 대리자를 만들 필요가 없습니다.
- 호텔은 언제 “작업”을하고 있는지 알고 있습니다. 잠금을 유지 한 상태에서 외부 코드를 실행하는 데 항상주의를 기울여야하지만 콜백에서 다른 잠금이 필요할 수있는 작업을 수행하지 않아야하는 발신자를 신뢰할 수있는 경우 메소드가 ‘CompareExchange’와 호환되지 않는 업데이트는 여전히 원자 적으로 수행 될 수 있습니다.
통과하는 것은 ref
훌륭한 패턴입니다. 너무 많이 사용하지 않습니다.
답변
Nathan의 Linq Expression 솔루션 으로 약간 확장되었습니다 . 속성이 문자열로 제한되지 않도록 다중 일반 매개 변수를 사용하십시오.
void GetString<TClass, TProperty>(string input, TClass outObj, Expression<Func<TClass, TProperty>> outExpr)
{
if (!string.IsNullOrEmpty(input))
{
var expr = (MemberExpression) outExpr.Body;
var prop = (PropertyInfo) expr.Member;
if (!prop.GetValue(outObj).Equals(input))
{
prop.SetValue(outObj, input, null);
}
}
}
답변
이것은 C # 언어 사양의 7.4.1 섹션에서 다룹니다. 변수 참조 만 인수 목록에서 ref 또는 out 매개 변수로 전달할 수 있습니다. 특성은 변수 참조로 규정되지 않으므로 사용할 수 없습니다.