C # में संदर्भ द्वारा पासिंग गुण


224

मैं निम्नलिखित करने की कोशिश कर रहा हूँ:

GetString(
    inputString,
    ref Client.WorkPhone)

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

यह मुझे एक संकलन त्रुटि दे रहा है। मुझे लगता है कि मैं इसे हासिल करने की कोशिश कर रहा हूं। मूल रूप से मैं चाहता हूँ GetStringके लिए एक इनपुट स्ट्रिंग की सामग्री की प्रतिलिपि करने के लिए WorkPhoneकी संपत्ति Client

क्या संदर्भ द्वारा एक संपत्ति पारित करना संभव है?


ऐसा क्यों है, इस stackoverflow.com/questions/564557/… पर
nawfal

जवाबों:


423

संपत्तियों को संदर्भ से पारित नहीं किया जा सकता है। इस सीमा के आसपास आप कुछ तरीके बता सकते हैं।

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

2
उदाहरणों से प्यार करो। मुझे लगता है कि यह विस्तार विधियों के लिए भी एक शानदार जगह है: codeसार्वजनिक स्थैतिक स्ट्रिंग GetValueOrDefault (यह स्ट्रिंग s, स्ट्रिंग NullString) {if (s == null) {s = isNullString; } रिटर्न एस; } void मेन () {person.MobilePhone.GetValueOrDefault (person.WorkPhone); }
ब्लैकजैकेटमैक

9
समाधान 2 में, 2 पैरामीटर getOutputअनावश्यक है।
जैदर

31
और मुझे लगता है कि समाधान 3 के लिए एक बेहतर नाम प्रतिबिंब है।
17

1
समाधान 2 में, दूसरा पैरामीटर getOutput अनावश्यक है - सच है लेकिन मैंने इसका उपयोग GetString के अंदर यह देखने के लिए किया था कि मूल्य जो मैं सेट कर रहा था। सुनिश्चित नहीं है कि इस पैरामीटर के बिना ऐसा कैसे करें।
पेट्रास

3
@GoneCodingGoodbye: लेकिन कम से कम कुशल दृष्टिकोण। किसी संपत्ति को केवल एक मूल्य प्रदान करने के लिए प्रतिबिंब का उपयोग करना अखरोट को दरार करने के लिए एक स्लेजहैमर लेने जैसा है। इसके अलावा, एक विधि GetStringजिसे किसी संपत्ति को सेट करना है, वह स्पष्ट रूप से गलत है।
टिम श्मेल्टर

27

संपत्ति की नकल के बिना

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

4
नाम बदलने के +1 GetStringको NullSafeSet, क्योंकि पूर्व कोई मतलब नहीं यहाँ बनाता है।
कैमिलो मार्टिन

25

मैंने एक्सप्रेशनट्री वेरिएंट और 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");

3
सबसे अच्छा जवाब यहाँ। क्या आप जानते हैं कि प्रदर्शन प्रभाव क्या है? यह अच्छा होगा कि इसे उत्तर के भीतर कवर किया जाए। मैं अभिव्यक्ति के पेड़ों से बहुत परिचित नहीं हूं, लेकिन मैं उम्मीद करूंगा कि कंपाइल () का उपयोग करने का मतलब है कि एक्सेसर एक्सेस में वास्तव में आईएल संकलित कोड है और इसलिए एन-बार एक्सेसर्स की निरंतर संख्या का उपयोग करना ठीक होगा, लेकिन कुल एन एक्सेसर्स का उपयोग करना ( उच्च ctor लागत) नहीं होगा।
mancze

महान कोड! मेरी राय, यह सबसे अच्छा जवाब है। सबसे सामान्य एक। जैसा कि मैन्कज कहते हैं ... इसका प्रदर्शन पर भारी प्रभाव होना चाहिए और इसका उपयोग केवल ऐसे संदर्भ में किया जाना चाहिए जहां कूट स्पष्टता पूर्णता से अधिक महत्वपूर्ण है।
एरिक ओउलेट

5

यदि आप संपत्ति को प्राप्त करना और सेट करना चाहते हैं, तो आप इसका उपयोग 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);
    }
}

3

एक और चाल अभी तक का उल्लेख नहीं किया वर्ग है जो एक संपत्ति को लागू करता है (जैसे 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. संपत्ति को पता है कि कब "के साथ काम किया जा रहा है"। जबकि ताला लगाते समय बाहरी कोड को निष्पादित करते समय सावधानी बरतना आवश्यक है, अगर कोई कॉल करने वाले पर भरोसा नहीं कर सकता है तो अपने कॉलबैक में ऐसा कुछ भी न करें जिसके लिए दूसरे लॉक की आवश्यकता हो, यह हो सकता है कि प्रॉपर्टी की पहरेदारी के लिए विधि का उपयोग करें। लॉक, ऐसे अपडेट जो `तुलनाएक्सचेंज` के साथ संगत नहीं हैं, फिर भी अर्ध-परमाणु प्रदर्शन किया जा सकता है।

चीजों को पास refकरना एक उत्कृष्ट पैटर्न है; बहुत बुरा यह अधिक उपयोग नहीं किया जाता है।


3

नाथन के लिनक अभिव्यक्ति समाधान के लिए बस थोड़ा विस्तार । मल्टी जेनेरिक परम का उपयोग करें ताकि संपत्ति स्ट्रिंग तक सीमित न हो।

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

2

यह C # भाषा युक्ति के खंड 7.4.1 में शामिल है। तर्क सूची में केवल एक चर-संदर्भ को रेफरी या आउट पैरामीटर के रूप में पारित किया जा सकता है। एक संपत्ति एक चर संदर्भ के रूप में योग्य नहीं है और इसलिए इसका उपयोग नहीं किया जा सकता है।


2

यह नहीं हो सकता। तुम कह सकते हो

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


1

आप जो करने की कोशिश कर सकते हैं वह संपत्ति के मूल्य को रखने के लिए एक वस्तु बनाने के लिए है। इस तरह से आप ऑब्जेक्ट को पास कर सकते हैं और अभी भी अंदर की संपत्ति तक पहुंच सकते हैं।


1

संपत्तियों को संदर्भ से पारित नहीं किया जा सकता है? इसे तब फ़ील्ड बनाएं, और सार्वजनिक रूप से इसे संदर्भित करने के लिए संपत्ति का उपयोग करें:

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

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

0

स्वीकृत जवाब अच्छा है यदि वह फ़ंक्शन आपके कोड में है और आप उसे संशोधित कर सकते हैं। लेकिन कभी-कभी आपको किसी बाहरी लाइब्रेरी से किसी ऑब्जेक्ट और फ़ंक्शन का उपयोग करना पड़ता है और आप संपत्ति और फ़ंक्शन की परिभाषा को बदल नहीं सकते हैं। तब आप बस एक अस्थायी चर का उपयोग कर सकते हैं।

var phone = Client.WorkPhone;
GetString(input, ref phone);
Client.WorkPhone = phone;

0

इस मुद्दे पर वोट करने के लिए, यहां एक सक्रिय सुझाव है कि इसे भाषा में कैसे जोड़ा जा सकता है। मैं यह नहीं कह रहा हूं कि ऐसा करने का सबसे अच्छा तरीका है (बिल्कुल भी), बेझिझक अपना सुझाव दें। लेकिन गुणों को विजुअल बेसिक की तरह ही पास करने की अनुमति देना पहले से ही कुछ कोड को सरल बनाने में मदद कर सकता है, और अक्सर!

https://github.com/dotnet/csharplang/issues/1235

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.