Frage

Ich versuche, das folgende zu tun tun:

GetString(
    inputString,
    ref Client.WorkPhone)

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

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

Ist es möglich, eine Eigenschaft durch Verweis übergeben?

War es hilfreich?

Lösung

Eigenschaften können nicht als Referenz übergeben werden. Hier sind ein paar 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. Delegieren

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 Expression

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

Andere Tipps

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

Ich schrieb einen Wrapper der ExpressionTree Variante und c # 7 (falls jemand interessiert):

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 verwenden Sie es mögen:

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

Ein weiterer Trick noch nicht erwähnt ist, um die Klasse zu haben, die eine Eigenschaft implementiert (zB Foo vom Typ Bar) auch einen Delegaten delegate void ActByRef<T1,T2>(ref T1 p1, ref T2 p2); definieren und ein Verfahren ActOnFoo<TX1>(ref Bar it, ActByRef<Bar,TX1> proc, ref TX1 extraParam1) (und möglicherweise auch Versionen für zwei und drei „zusätzliche Parameter“ als auch) zu implementieren, die wird ihre interne Darstellung Foo die zugeführten Prozedur als ref Parameter. Dies hat ein paar große Vorteile gegenüber anderen Methoden der mit der Eigenschaft arbeiten:

  1. Die Eigenschaft wird aktualisiert "in place"; wenn die Eigenschaft eines Typs, der mit `Interlocked` Methoden kompatibel ist, oder wenn es sich um eine Struktur mit freiliegenden Bereichen solcher Typen ist, werden die` Interlocked` verwendeten Methoden können atomare Aktualisierungen der Eigenschaft auszuführen.
  2. Wenn die Eigenschaft ist eine exponierter-Feldstruktur, die Felder der Struktur kann, ohne modifiziert werden keine redundanten Kopien davon zu machen.
  3. Wenn die `ActByRef` Methode übergibt eine oder mehrere` ref` Parameter durch von seinem Anrufer mit dem mitgelieferten Delegierten, kann es möglich sein, ein Singleton oder statische Delegierten zu verwenden, wodurch die Notwendigkeit vermieden wird Schließungen zu erstellen oder Delegierten zur Laufzeit.
  4. Die Eigenschaft weiß, wann es „gearbeitet“ wird. Während es immer notwendig ist Vorsicht Ausführung von externem Code zu verwenden, während eine Sperre hält, wenn man Anrufer nicht vertrauen, etwas zu tun in ihrem Rückruf zu tun, die eine andere Sperre erforderlich machen könnten, kann es sinnvoll sein, das Verfahren für den Zutritt die Eigenschaft zu haben, mit einem zu sperren, so dass Aktualisierungen, die mit nicht kompatibel sind `CompareExchange` noch quasi-atomar ausgeführt werden kann.

Passing Dinge ref ist ein ausgezeichnetes Muster; schade, dass es nicht mehr verwendet wird.

Nur eine kleine Erweiterung auf Nathans Linq Expression Lösung . Verwenden Sie Multi generic param so dass die Eigenschaft nicht auf Zeichenfolge nicht beschränkt.

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

Wenn Sie sowohl die Immobilie zu erhalten und zu installieren, können Sie diese in C verwenden # 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);
    }
}

Dies ist in Abschnitt 7.4.1 der Sprache C # spec abgedeckt. Nur eine variabler Referenz kann als ref oder out-Parameter in einer Argumentliste übergeben werden. Eine Eigenschaft, nicht als variablen Referenz qualifizieren und somit nicht verwendet werden kann.

Dies ist nicht möglich. Man könnte sagen,

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

wo WorkPhone ist eine beschreibbare string Eigenschaft und die Definition von GetString geändert, um

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

Dies wird die gleiche Semantik, die Sie scheinen zu versuchen.

Dies ist nicht möglich, weil eine Eigenschaft wirklich ein Paar von Methoden in der Verkleidung ist. Jede Eigenschaft zur Verfügung stellt Getter und Setter, die über Feld-ähnliche Syntax zugänglich sind. Wenn Sie versuchen, rufen GetString wie Sie vorgeschlagen haben, was vorbei sind Sie in ein Wert ist und nicht eine Variable. Der Wert, den Sie übergeben in ist, dass aus dem Getter get_WorkPhone zurückgegeben.

Was könnten Sie versuchen zu tun ist, ein Objekt zu erstellen, den Eigenschaftswert zu halten. Auf diese Weise können Sie das Objekt passieren konnte und nach wie vor innerhalb Zugriff auf die Eigenschaft haben.

Sie können nicht eine Eigenschaft ref, aber wenn Ihre Funktionen sowohl get und set Zugang benötigen, können Sie eine Instanz einer Klasse mit einer Eigenschaft übergeben um definiert:

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

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

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
    }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top