Przekazywanie właściwości przez odniesienie w C#
Staram się wykonać następujące czynności:
GetString(
inputString,
ref Client.WorkPhone)
private void GetString(string inValue, ref string outValue)
{
if (!string.IsNullOrEmpty(inValue))
{
outValue = inValue;
}
}
To daje mi błąd kompilacji. Myślę, że to jasne, co próbuję osiągnąć. Zasadniczo chcę GetString
skopiować zawartość łańcucha wejściowego do Właściwości WorkPhone
Client
.
Czy można przekazać nieruchomość przez odniesienie?
10 answers
Właściwości nie mogą być przekazywane przez odniesienie. Oto kilka sposobów obejścia tego ograniczenia.
1. Return Value
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. Delegat
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. Wyrażenie 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. Reflection
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");
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-28 16:04:13
Bez powielania właściwości
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);
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-24 09:47:30
Napisałem wrapper używając wariantu ExpressionTree i c#7 (jeśli ktoś jest zainteresowany):
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();
}
I używaj go jak:
var accessor = new Accessor<string>(() => myClient.WorkPhone);
accessor.Set("12345");
Assert.Equal(accessor.Get(), "12345");
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-19 14:40:58
Inną sztuczką, o której jeszcze nie wspomniano, jest posiadanie klasy, która implementuje właściwość (np. Foo
typu Bar
) również definiuje delegata delegate void ActByRef<T1,T2>(ref T1 p1, ref T2 p2);
i implementuje metodę ActOnFoo<TX1>(ref Bar it, ActByRef<Bar,TX1> proc, ref TX1 extraParam1)
(i ewentualnie wersje dla dwóch i trzech "dodatkowych parametrów"), która przekaże swoją wewnętrzną reprezentację Foo
do dostarczonej procedury jako parametr ref
. Ma to kilka dużych zalet w stosunku do innych metod pracy z właściwością:
- nieruchomość jest aktualizowana "na miejscu"; jeśli nieruchomość jest typu zgodnego z metodami "Interlocked" lub jeśli jest strukturą z odsłoniętymi polami takich typów, metody "Interlocked" mogą być użyte do wykonywania atomowych aktualizacji właściwości.
- jeśli właściwość jest strukturą exposed-field, pola tej struktury mogą być modyfikowane bez konieczności tworzenia jej zbędnych kopii.
- jeśli metoda 'ActByRef' przekazuje jeden lub więcej parametrów `ref` od wywołującego do dostarczonego delegata, może być możliwe użyj pojedynczego lub statycznego delegata, unikając w ten sposób konieczności tworzenia zamknięć lub delegatów w czasie wykonywania.
- nieruchomość wie, kiedy jest "obrabiana". Chociaż zawsze konieczne jest zachowanie ostrożności podczas wykonywania zewnętrznego kodu podczas trzymania blokady, jeśli można zaufać wywołującym, że nie zrobią zbyt wiele w ich wywołaniu zwrotnym, które może wymagać innej blokady, może być praktyczne, aby metoda chroniła dostęp do właściwości za pomocą blokady, tak aby aktualizacje, które nie są zgodne z `CompareExchange " może być nadal wykonywane quasi-atomicznie.
Przechodzenie rzeczy be ref
to doskonały wzór; szkoda, że nie jest używany więcej.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-16 21:37:13
Jest to opisane w sekcji 7.4.1 specyfikacji języka C#. Tylko zmienna-reference może być przekazywana jako parametr ref lub out na liście argumentów. Właściwość nie kwalifikuje się jako odniesienie do zmiennej i dlatego nie może być używana.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-10 00:27:23
To niemożliwe. Można powiedzieć
Client.WorkPhone = GetString(inputString, Client.WorkPhone);
Gdzie WorkPhone
jest zapisywalną właściwością string
, a definicja {[4] } zmienia się na
private string GetString(string input, string current) {
if (!string.IsNullOrEmpty(input)) {
return input;
}
return current;
}
Będzie to miało tę samą semantykę, o którą się starasz.
Nie jest to możliwe, ponieważ właściwość jest naprawdę parą metod w ukryciu. Każda właściwość udostępnia gettery i settery, które są dostępne za pomocą składni podobnej do pól. Kiedy próbujesz zadzwonić GetString
tak, jak zaproponowałeś, to, co przekazujesz, jest wartością i nie zmienna. Wartość, którą przekazujesz, jest zwracana z gettera get_WorkPhone
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-18 08:49:46
Małe rozszerzenie doRozwiązania LINQ . Użyj multi generic param, aby właściwość nie ograniczała się do ciągu znaków.
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);
}
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-18 08:50:15
Możesz spróbować utworzyć obiekt do przechowywania wartości właściwości. W ten sposób możesz przejść przez obiekt i nadal mieć dostęp do nieruchomości w środku.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-02 15:58:14
Jeśli chcesz uzyskać i ustawić obie właściwości, możesz użyć tego w 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);
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-22 03:16:05
Nie możesz ref
właściwości, ale jeśli twoje funkcje potrzebują zarówno dostępu get
, jak i set
, możesz przekazać instancję klasy o zdefiniowanej właściwości:
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;
}
}
Przykład:
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);
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-08 20:11:54