سؤال

أحاول القيام بما يلي:

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 البديل وج # 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) (وربما إصدارات لاثنين وثلاثة "معلمات إضافية" أيضًا) والتي ستمرر تمثيلها الداخلي لـ Foo إلى الإجراء المقدم ك ref معامل.يتمتع هذا بميزتين كبيرتين مقارنةً بالطرق الأخرى للعمل مع العقار:

  1. يتم تحديث الخاصية "في مكانها"؛إذا كانت الخاصية من النوع المتوافق مع الأساليب "المتشابكة"، أو إذا كانت عبارة عن بنية تحتوي على حقول مكشوفة من هذه الأنواع، فيمكن استخدام الأساليب "المتشابكة" لإجراء تحديثات ذرية للخاصية.
  2. إذا كانت الخاصية عبارة عن بنية حقل مكشوفة، فقد يتم تعديل حقول البنية دون الحاجة إلى عمل أي نسخ متكررة منها.
  3. إذا قام الأسلوب "ActByRef" بتمرير واحد أو أكثر من معلمات "ref" من المتصل الخاص به إلى المفوض المقدم، فقد يكون من الممكن استخدام مفوض فردي أو ثابت، وبالتالي تجنب الحاجة إلى إنشاء عمليات إغلاق أو تفويض في وقت التشغيل.
  4. يعرف العقار متى يتم "التعامل معه".على الرغم من أنه من الضروري دائمًا توخي الحذر عند تنفيذ التعليمات البرمجية الخارجية أثناء الضغط على القفل، إذا كان من الممكن الوثوق بالمتصلين بعدم القيام بأي شيء في رد الاتصال الخاص بهم قد يتطلب قفلًا آخر، فقد يكون من العملي أن تقوم الطريقة بحماية وصول الخاصية باستخدام القفل، بحيث لا يزال من الممكن إجراء التحديثات غير المتوافقة مع `CompareExchange` بشكل شبه ذري.

الأشياء العابرة تكون ref هو نمط ممتاز.سيئة للغاية أنها لا تستخدم أكثر.

ومجرد التوسع قليلا ل ناثان ينق حل التعبير . استخدام متعدد المعلمة عام بحيث الخاصية لا تقتصر على السلسلة.

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

وهذا هو تغطية في القسم 7.4.1 من المواصفات C # لغة. على بعد مرجع متغير يمكن أن تنتقل بمثابة المرجع أو المعلمة في قائمة وسيطة. خاصية ليست مؤهلة كمرجع متغير وبالتالي لا يمكن استخدامها.

وهذا غير ممكن. هل يمكن أن نقول

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 كما كنت قد اقترحت، ما كنت تمر في غير قيمة وليس متغير. القيمة التي يتم تمريرها في أن عاد من 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
    }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top