it-swarm.com.de

Eigenschaften als Referenz in C # übergeben

Ich versuche folgendes zu tun:

GetString(
    inputString,
    ref Client.WorkPhone)

private void GetString(string inValue, ref string outValue)
{
    if (!string.IsNullOrEmpty(inValue))
    {
        outValue = inValue;
    }
}

Das gibt mir einen Kompilierungsfehler. Ich denke, es ist ziemlich klar, was ich erreichen möchte. Grundsätzlich möchte GetString den Inhalt einer Eingabezeichenfolge in die WorkPhone-Eigenschaft von Client kopieren.

Kann eine Immobilie per Referenz übergeben werden?

189
yogibear

Eigenschaften können nicht als Referenz übergeben werden. Hier sind einige Möglichkeiten, wie Sie diese Einschränkung umgehen können.

1. Rückgabewert

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. Delegierter

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-Ausdruck

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. Reflexion

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");
}
358
Nathan Baulch

ohne die Eigenschaft zu duplizieren

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);
    }
}
23
Firo

Ich habe einen Wrapper geschrieben, der die ExpressionTree-Variante und c # 7 verwendet (wenn jemand interessiert ist):

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();
}

Und benutze es wie:

var accessor = new Accessor<string>(() => myClient.WorkPhone);
accessor.Set("12345");
Assert.Equal(accessor.Get(), "12345");
16
Sven

Ein weiterer Trick, der noch nicht erwähnt wurde, besteht darin, dass die Klasse, die eine Eigenschaft implementiert (z. B. Foo vom Typ Bar), auch einen Delegierten delegate void ActByRef<T1,T2>(ref T1 p1, ref T2 p2); definiert und eine Methode ActOnFoo<TX1>(ref Bar it, ActByRef<Bar,TX1> proc, ref TX1 extraParam1) implementiert (und möglicherweise auch Versionen für zwei und drei "zusätzliche Parameter"), die dessen Parameter übergeben interne Darstellung von Foo in der angegebenen Prozedur als ref-Parameter. Dies hat gegenüber anderen Methoden, mit der Eigenschaft zu arbeiten, einige große Vorteile:

  1. Die Eigenschaft wird "vor Ort" aktualisiert. Wenn es sich bei der Eigenschaft um einen Typ handelt, der mit `Interlocked`-Methoden kompatibel ist, oder wenn es sich um eine Struktur mit exponierten Feldern solcher Typen handelt, können die` Interlocked'-Methoden verwendet werden, um atomare Aktualisierungen der Eigenschaft durchzuführen.
  2. Wenn es sich bei der Eigenschaft um eine exponierte Feldstruktur handelt, können die Felder der Struktur geändert werden, ohne dass redundante Kopien davon erstellt werden müssen.
  3. Wenn die `ActByRef'-Methode einen oder mehrere` ref`-Parameter von ihrem Aufrufer an den angegebenen Delegaten durchlässt, kann es möglich sein, einen Singleton- oder einen statischen Delegaten zu verwenden, sodass keine Schließungen oder Delegaten zur Laufzeit erstellt werden müssen
  4. Die Eigenschaft weiß, wann mit "bearbeitet" wird. Zwar ist beim Ausführen einer Sperre immer Vorsicht geboten, wenn externer Code ausgeführt wird. Wenn Anrufer jedoch nicht dazu in der Lage sind, in ihrem Rückruf irgendetwas zu tun, das möglicherweise eine weitere Sperre erfordert, kann es zweckmäßig sein, dass die Methode den Eigenschaftszugriff mit a überwacht sperren, so dass Aktualisierungen, die nicht mit `CompareExchange` kompatibel sind, quasi-atomar durchgeführt werden können.

Das Weitergeben von Dingen ref ist ein ausgezeichnetes Muster; schade, dass es nicht mehr verwendet wird.

3
supercat

Nur eine kleine Erweiterung zu Nathans Linq Expression-Lösung . Verwenden Sie mehrere generische Parameter, damit die Eigenschaft nicht auf Zeichenfolgen beschränkt ist.

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);
        }
    }
}
3
Zick Zhang

Das ist nicht möglich. Du könntest sagen

Client.WorkPhone = GetString(inputString, Client.WorkPhone);

wobei WorkPhone eine beschreibbare string-Eigenschaft ist und die Definition von GetString in geändert wird

private string GetString(string input, string current) { 
    if (!string.IsNullOrEmpty(input)) {
        return input;
    }
    return current;
}

Dies hat die gleiche Semantik, die Sie anscheinend versuchen.

Dies ist nicht möglich, da es sich bei einer Eigenschaft tatsächlich um verschleierte Methoden handelt. Jede Eigenschaft stellt Getter und Setter zur Verfügung, die über eine feldartige Syntax erreichbar sind. Wenn Sie versuchen, GetString wie vorgeschlagen aufzurufen, übergeben Sie einen Wert und keine Variable. Der Wert, den Sie übergeben, ist der vom Getter get_WorkPhone zurückgegebene Wert.

2
jason

Dies wird in Abschnitt 7.4.1 der C # -Sprachenspezifikation behandelt. In einer Argumentliste kann nur eine Variablenreferenz als ref- oder out-Parameter übergeben werden. Eine Eigenschaft ist nicht als Variablenreferenz qualifiziert und kann daher nicht verwendet werden. 

2
JaredPar

Wenn Sie beide Eigenschaften abrufen und festlegen möchten, können Sie dies in C # 7 verwenden:

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);
    }
}
1
Pellet

Sie könnten versuchen, ein Objekt zu erstellen, das den Eigenschaftswert enthält. Auf diese Weise könnten Sie das Objekt übergeben und haben weiterhin Zugriff auf die Eigenschaft im Inneren.

1
Anthony Reese

Sie können keine ref-Eigenschaft angeben, aber wenn Ihre Funktionen sowohl get- als auch set-Zugriff benötigen, können Sie eine Instanz einer Klasse mit einer definierten Eigenschaft übergeben:

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;
    }
}

Beispiel:

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);
    }
}
0
chess123mate

Eigenschaften können nicht als Referenz übergeben werden? Machen Sie es dann zu einem Feld und verwenden Sie die Eigenschaft, um es öffentlich zu referenzieren:

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
    }
}
0
macedo123