C#에서 참조로 특성 전달
-
05-07-2019 - |
문제
나는 다음을 수행하려고 노력하고 있습니다.
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))
{
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");
아직 언급되지 않은 또 다른 속임수 Foo
유형의 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
제공된 절차에 a ref
매개 변수. 이것은 재산과 협력하는 다른 방법에 비해 몇 가지 큰 장점이 있습니다.
- 속성은 "제자리에"업데이트됩니다. 속성이 '인터 로크'메소드와 호환되는 유형이거나 해당 유형의 노출 된 필드가있는 구조물 인 경우 '인터 로크'방법을 사용하여 속성에 대한 원자력 업데이트를 수행 할 수 있습니다.
- 속성이 노출 된 필드 구조 인 경우, 중복 사본을 만들지 않고도 구조의 필드를 수정할 수 있습니다.
- `ActByRef` 메소드가 발신자에서 제공된 대리인으로 하나 이상의 'ref'매개 변수를 통과하는 경우, 싱글 톤 또는 정적 대리인을 사용할 수 있으므로 런타임에 폐쇄 또는 대의원을 생성 할 필요가 없습니다.
- 이 부동산은 언제 "함께 일할 것인지"알고 있습니다. 잠금 장치를 잡고있는 동안 외부 코드를 실행하는 것이 항상 필요하지만, 발신자가 다른 잠금이 필요한 콜백에서 아무것도하지 않도록 신뢰할 수 있다면, 메소드가 속성에 액세스 할 수있는 것이 실용적 일 수 있습니다. 'CompareexChange'와 호환되지 않는 업데이트는 여전히 준 원자로를 수행 할 수 있습니다.
전달되는 것 ref
훌륭한 패턴입니다. 더 이상 사용되지 않는다.
약간의 확장 Nathan의 LINQ 발현 솔루션. 속성이 문자열로 제한되지 않도록 멀티 generic param을 사용하십시오.
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에서 사용할 수 있습니다.
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);
}
}
이것은 C# 언어 사양의 7.4.1 절에서 다룹니다. 인수 목록에서 변수 참조 만 참조 또는 출력 매개 변수로 전달 될 수 있습니다. 속성은 가변 참조 자격이 없으므로 사용할 수 없습니다.
이건 불가능 해. 넌 말할 수있다
Client.WorkPhone = GetString(inputString, Client.WorkPhone);
어디 WorkPhone
쓰기 가능합니다 string
속성과 정의 GetString
변경됩니다
private string GetString(string input, string current) {
if (!string.IsNullOrEmpty(input)) {
return input;
}
return current;
}
이것은 당신이 시도하는 것과 같은 의미를 가질 것입니다.
속성이 실제로 변장하는 한 쌍의 방법이기 때문에 불가능합니다. 각 속성은 필드와 같은 구문을 통해 액세스 할 수있는 게이터와 세터를 사용할 수 있습니다. 전화를 시도 할 때 GetString
당신이 제안한 바와 같이, 당신이 통과하는 것은 변수가 아니라 값입니다. 당신이 통과하는 가치는 getter에서 돌아온 것입니다. get_WorkPhone
.
당신이 시도 할 수있는 것은 속성 값을 보유 할 객체를 만드는 것입니다. 그렇게하면 객체를 통과 할 수 있고 여전히 내부의 속성에 액세스 할 수 있습니다.
당신은 할 수 없습니다 ref
속성이지만 기능에 둘 다 필요합니다 get
그리고 set
액세스 속성이 정의 된 클래스 인스턴스를 전달할 수 있습니다.
public class Property<T>
{
public delegate T Get();
public delegate void Set(T value);
private Get get;
private Set set;
public T Value {
get {
return get();
}
set {
set(value);
}
}
public Property(Get get, Set set) {
this.get = get;
this.set = set;
}
}
예시:
class Client
{
private string workPhone; // this could still be a public property if desired
public readonly Property<string> WorkPhone; // this could be created outside Client if using a regular public property
public int AreaCode { get; set; }
public Client() {
WorkPhone = new Property<string>(
delegate () { return workPhone; },
delegate (string value) { workPhone = value; });
}
}
class Usage
{
public void PrependAreaCode(Property<string> phone, int areaCode) {
phone.Value = areaCode.ToString() + "-" + phone.Value;
}
public void PrepareClientInfo(Client client) {
PrependAreaCode(client.WorkPhone, client.AreaCode);
}
}
속성은 참조로 전달할 수 없습니까? 그런 다음 필드로 만들고 속성을 사용하여 공개적으로 참조하십시오.
public class MyClass
{
public class MyStuff
{
string foo { get; set; }
}
private ObservableCollection<MyStuff> _collection;
public ObservableCollection<MyStuff> Items { get { return _collection; } }
public MyClass()
{
_collection = new ObservableCollection<MyStuff>();
this.LoadMyCollectionByRef<MyStuff>(ref _collection);
}
public void LoadMyCollectionByRef<T>(ref ObservableCollection<T> objects_collection)
{
// Load refered collection
}
}