2014-07-24 9 views
13

this question के समान, मैं वैकल्पिक पैरामीटर को पैराम कीवर्ड के साथ मिश्रित करना चाहता हूं, जो निश्चित रूप से अस्पष्टता पैदा करता है। दुर्भाग्य से, बनाने भार के का जवाब काम नहीं करता है, जैसा कि मैंने फोन करने वाले की जानकारी का लाभ लेने के लिए जिम्मेदार बताते हैं, इस तरह हैं:वैकल्पिक पैरामीटर और पैराम्स को मिलाकर

public void Info(string message, [CallerMemberName] string memberName = "", 
        [CallerLineNumber] int lineNumber = 0, params object[] args) 
    { 
     _log.Info(BuildMessage(message, memberName, lineNumber), args); 
    } 

वैकल्पिक पैरामीटर के बिना एक अधिभार बनाना कॉल-साइट बदल जाएगा, इन विशेष मानकों को रोकने ठीक से काम करने से।

मैं एक समाधान है कि लगभग काम करता है (हालांकि यह बदसूरत है) पाया:

public void Info(string message, object arg0, [CallerMemberName] string memberName = "", 
        [CallerLineNumber] int lineNumber = 0) 
    { 
     _log.Info(BuildMessage(message, memberName, lineNumber), arg0); 
    } 

    public void Info(string message, object arg0, object arg1, [CallerMemberName] string memberName = "", 
        [CallerLineNumber] int lineNumber = 0) 
    { 
     _log.Info(BuildMessage(message, memberName, lineNumber), arg0, arg1); 
    } 

समस्या यहाँ अगर आप अंतिम तर्क के लिए एक स्ट्रिंग निर्दिष्ट, अधिभार संकल्प मानता है कि आप स्पष्ट रूप से निर्दिष्ट करने का इरादा कर रहे हैं memberName अधिभार में जो कम तर्क लेता है, जो वांछित व्यवहार नहीं है।

क्या इसे पूरा करने का कोई तरीका है (शायद कुछ नए विशेषताओं का उपयोग करके मैंने नहीं सीखा है?) या क्या हम ऑटो-जादुई कंपाइलर समर्थन हमें क्या दे सकते हैं इसकी सीमा तक पहुंच गए हैं?

+0

हम ठीक उसी कारणों से इस काम करने की कोशिश की और विफल रहे। – CSharpie

+1

ध्यान में रखते हुए कि यह 'कॉलरइन्फो' के लिए मुख्य उपयोग-मामला है, यह विशेषता समाधान केवल भयानक है। – nicodemus13

+1

आपको केवल एक अतिरिक्त प्रकार (और कुछ सामान्य तरीकों) की आवश्यकता है। मैंने इस समस्या का एक आशाजनक संतोषजनक उत्तर प्रदान किया है। –

उत्तर

16

मेरे पसंदीदा तरीका: केवल दो charachters भूमि के ऊपर - बदसूरत भाषा हालांकि 'हैक';

public delegate void WriteDelegate(string message, params object[] args); 

public static WriteDelegate Info(
     [CallerMemberName] string memberName = "", 
     [CallerLineNumber] int lineNumber = 0) 
{ 
    return new WriteDelegate ((message,args)=> 
    { 
     _log.Info(BuildMessage(message, memberName , lineNumber), args); 
    }); 
} 

प्रयोग (BuildMessage

Info()("hello world {0} {1} {2}",1,2,3); 

वैकल्पिक

तरह से मेरी सहयोगी इस काम करने के लिए आया था की अपनी खुद की कार्यान्वयन की आपूर्ति इस तरह था:

public static class DebugHelper 

    public static Tuple<string,int> GetCallerInfo(
     [CallerMemberName] string memberName = "", 
     [CallerLineNumber] int lineNumber = 0) 
    { 
     return Tuple.Create(memberName,lineNumber); 
    } 
} 

InfoMethod:

public void Info(Tuple<string,int> info, string message, params object[] args) 
{ 
     _log.Info(BuildMessage(message, info.Item1, info.Item2), args); 
} 

उपयोग:

instance.Info(DebugHelper.GetCallerInfo(),"This is some test {0} {1} {2}",1,2,3); 
+0

इसने मुझे जिस दृष्टिकोण को घायल कर दिया, उसे प्रेरित किया, इसलिए इसे उत्तर के रूप में चुनना। –

+0

मैं अपने पैटर्नलेआउट में कॉलर इन्फॉर्मेशन को आगे बढ़ाने के लिए थ्रेड कॉन्टेक्स्ट-प्रॉपर्टी का उपयोग करना चाहता हूं जैसा कि मैंने यहां बताया है: [link] (http://stackoverflow.com/a/42544380/665689) – Ingo

1

रास्ता 1.

मैं आप StackFrameCallerLineNumber के बजाय का उपयोग कर सकते हैं:

public void Info(string message, params object[] args) 
{ 
    StackFrame callStack = new StackFrame(1, true); 
    string memberName = callStack.GetMethod().Name; 
    int lineNumber = callStack.GetFileLineNumber(); 
    _log.Info(BuildMessage(message, memberName, lineNumber), args); 
} 

उपयोगी ऐसे कई पृष्ठ:

रास्ता 2।

public class InfoMessage 
{ 
    public string Message { get; private set; } 
    public string MemberName { get; private set; } 
    public int LineNumber { get; private set; } 

    public InfoMessage(string message, 
        [CallerMemberName] string memberName = "", 
        [CallerLineNumber] int lineNumber = 0) 
    { 
    Message = message; 
    MemberName = memberName; 
    LineNumber = lineNumber; 
    } 
} 

public void Info(InfoMessage infoMessage, params object[] args) 
{ 
    _log.Info(BuildMessage(infoMessage), args); 
} 

public string BuildMessage(InfoMessage infoMessage) 
{ 
    return BuildMessage(infoMessage.Message, 
    infoMessage.MemberName, infoMessage.LineNumber); 
} 

void Main() 
{ 
    Info(new InfoMessage("Hello")); 
} 
+4

ध्यान रखें कि इसमें एक प्रदर्शन किया गया है, क्योंकि कॉलस्टैक का निर्माण उन फैंसी विशेषताओं के निरंतर मूल्यों की तुलना में अधिक महंगा है। – CSharpie

+0

@CSharpie, मैंने रास्ता जोड़ा 2. – AndreyAkinshin

3

जवाब दूसरों प्रदान की के आधार पर, मैं देख सकता हूँ पहले संदर्भ पर कब्जा है, तो साथ प्रवेश विधि लागू पर है कि वे बड़े पैमाने पर आधारित हैं कब्जा कर लिया संदर्भ।

public CallerContext Info([CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) 
    { 
     return new CallerContext(_log, LogLevel.Info, memberName, lineNumber); 
    } 

    public struct CallerContext 
    { 
     private readonly Logger _logger; 
     private readonly LogLevel _level; 
     private readonly string _memberName; 
     private readonly int _lineNumber; 

     public CallerContext(Logger logger, LogLevel level, string memberName, int lineNumber) 
     { 
      _logger = logger; 
      _level = level; 
      _memberName = memberName; 
      _lineNumber = lineNumber; 
     } 

     public void Log(string message, params object[] args) 
     { 
      _logger.Log(_level, BuildMessage(message, _memberName, _lineNumber), args); 
     } 

     private static string BuildMessage(string message, string memberName, int lineNumber) 
     { 
      return memberName + ":" + lineNumber + "|" + message; 
     } 
    } 

आप एक LoggerProxy (वर्ग को परिभाषित विधि Info()) लॉग नामित किया है, उपयोग इस तरह है::

Log.Info().Log("My Message: {0}", arg); 

वाक्य रचना मेरे लिए थोड़ा क्लीनर लगता है (लॉग नकल मैं इस के साथ आया था अभी भी बदसूरत है, लेकिन इसलिए यह जाता है) और मुझे लगता है कि संदर्भ के लिए एक स्ट्रक्चर का उपयोग करके इसे प्रदर्शन के रूप में थोड़ा बेहतर बना दिया जा सकता है, हालांकि मुझे यह सुनिश्चित करने के लिए प्रोफाइल करना होगा।

6

तो, मैं वास्तव में इस समस्या में भाग गया लेकिन एक अलग कारण के लिए। आखिरकार मैंने इसे इस तरह हल किया।

पहला, सी # में अधिभार संकल्प (जेनेरिक तरीके आदर्श उम्मीदवार हैं)। मैंने 9 तर्कों के समर्थन के साथ इन एक्सटेंशन विधि ओवरलोड को उत्पन्न करने के लिए टी 4 का उपयोग किया। यहां केवल 3 तर्कों के साथ एक उदाहरण है।

public static void WriteFormat<T1, T2, T3>(this ILogTag tag, string format, T1 arg0, T2 arg1, T3 arg2 
    , [CallerMemberName] string callerMemberName = null, [CallerFilePath] string callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0 
    ) 
{ 
    if (tag != null) 
    { 
     var entry = new LogEntry(DateTimeOffset.Now, tag.TagName, new LogString(format, new object[] { arg0, arg1, arg2 }), callerMemberName, System.IO.Path.GetFileName(callerFilePath), callerLineNumber); 
     tag.Write(entry); 
    } 
} 

कौन सा थोड़ी देर के लिए ठीक काम करता है लेकिन अंत में एक अस्पष्टता में परिणाम है जब आप तर्क से मेल खाने वाले फोन करने वाले की जानकारी विशेषता सूची के किसी भी संयोजन का उपयोग करें। ऐसा होने से रोकने के लिए आपको वैकल्पिक पैरामीटर सूची की रक्षा करने के लिए एक प्रकार की आवश्यकता है और इसे वैकल्पिक पैरामीटर सूची से अलग करें।

एक खाली संरचना ठीक करेगी (मैं ऐसी चीजों के लिए लंबे और वर्णनात्मक नामों का उपयोग करता हूं)।

/// <summary> 
/// The purpose of this type is to act as a guard between 
/// the actual parameter list and optional parameter list. 
/// If you need to pass this type as an argument you are using 
/// the wrong overload. 
/// </summary> 
public struct LogWithOptionalParameterList 
{ 
    // This type has no other purpose. 
} 

नोट: मैं एक निजी निर्माता के साथ इस एक अमूर्त वर्ग बनाने के बारे में सोचा था, लेकिन वह वास्तव में nullLogWithOptionalParameterList प्रकार के रूप में पारित किया जा करने की अनुमति होगी। struct में यह समस्या नहीं है।

वास्तविक पैरामीटर सूची और वैकल्पिक पैरामीटर सूची के बीच इस प्रकार को सम्मिलित करें।

public static void WriteFormat<T1, T2, T3>(this ILogTag tag, string format, T1 arg0, T2 arg1, T3 arg2 
    , LogWithOptionalParameterList _ = default(LogWithOptionalParameterList) 
    , [CallerMemberName] string callerMemberName = null, [CallerFilePath] string callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0 
    ) 
{ 
    if (tag != null) 
    { 
     var entry = new LogEntry(DateTimeOffset.Now, tag.TagName, new LogString(format, new object[] { arg0, arg1, arg2 }), callerMemberName, System.IO.Path.GetFileName(callerFilePath), callerLineNumber); 
     tag.Write(entry); 
    } 
} 

वोला!

इस प्रकार का एकमात्र उद्देश्य ओवरलोड रिज़ॉल्यूशन प्रक्रिया के साथ गड़बड़ करना है, लेकिन यदि आप अपनी विधियों को लेते समय कॉलर जानकारी विशेषता मान (जिसे संकलक प्रदान किया जाना चाहिए) में गलती से भरने में एक संकलक त्रुटि भी होगी अतिरिक्त पैरामीटर मेरे पास ऐसी कुछ कॉल थीं जिसके परिणामस्वरूप तुरंत कंपाइलर त्रुटियां हुईं।

+1

ओवरलोड रिज़ॉल्यूशन समस्याओं को रोकने के लिए बहुत चालाक छोटी चाल । भावी भ्रम को रोकने में मदद के लिए निश्चित रूप से कोड में कुछ अच्छी टिप्पणियों की आवश्यकता होगी। :) –

+0

टिप्पणियों के संबंध में, मैंने 'LogFormatWithOptionalParameterList' प्रकार' पर एक टिप्पणी छोड़ दी। उम्मीद है कि कोई भी उस कंपाइलर त्रुटि में कभी भी चल रहा है तुरंत उस प्रकार की टिप्पणी की जांच करेगा। मैंने वास्तविक टिप्पणी के साथ अपना जवाब अपडेट कर दिया है। –

+0

एक ही समस्या थी, एक समान समाधान के साथ आया, एक "स्टॉपर" वैकल्पिक पैरामीटर, एक निर्विवाद वर्ग का उपयोग कर। मैं सभी वर्णनात्मक नामों के लिए भी हूं, लेकिन मेरा ('यह ट्रांसपेर पैरावेन्ट्स एक्सीडेंटल कॉल्सऑवरलोड्ससचैट कैलरइन्फोनेट्ससेट') निश्चित रूप से रिलीज से पहले कुछ ट्रिमिंग की ज़रूरत है। Btw। मुझे परवाह नहीं है कि मेरी निर्विवाद वर्ग को शून्य के रूप में प्रदान किया जा सकता है। समस्या कॉलर जानकारी को रोक रहा है * अनसुलझा * कॉलर्स से तर्कों को दूर करने से बहस करता है। क्या मैं वास्तव में देखभाल करता हूं कि यह उसी कारण से पहले स्थान पर निर्विवाद है। और: ** 'इस ILogTag' चाल के लिए धन्यवाद ** –

2

यदि आप अपने "बदसूरत समाधान" में अपने प्रारूप पैरामीटर वैकल्पिक बनाते हैं तो आपको प्रत्येक पैरामीटर के लिए भाषण अधिभार की आवश्यकता नहीं है, लेकिन केवल एक ही पर्याप्त है! उदाहरण के लिए:

public void Info(string message, object arg0=null, object arg1=null, 
[CallerMemberName] string memberName = "",[CallerLineNumber] int lineNumber = 0) 
{ 
    _log.Info(BuildMessage(message, memberName, lineNumber), arg0, arg1); 
} 

तो आप इसे करने के लिए तीन मापदंडों के साथ कॉल कर सकते हैं यानी

Info("No params"); 
Info("One param{0}",1); 
Info("Two param {0}-{1}",1,2); 

आप आसानी से आप कभी होगा भी बहुत कुछ वैकल्पिक formating तर्क जोड़कर गलती से CallerMemberName और CallerLineNumber भरने के जोखिम को कम कर सकते हैं उदाहरण की आवश्यकता है arg0, ... arg20।

या आप जॉन Leidegren समाधान यानी guarging पैरामीटर जोड़ने के साथ संयोजित कर सकते हैं .... argsX और पिछले दो पैरामीटर के बीच ...

संबंधित मुद्दे