अनाम प्रकार को C # में वापस करना


100

मेरे पास एक क्वेरी है जो एक अनाम प्रकार देता है और क्वेरी एक विधि में है। आप इसे कैसे लिखते हैं:

public "TheAnonymousType" TheMethod(SomeParameter)
{
  using (MyDC TheDC = new MyDC())
  {
     var TheQueryFromDB = (....
                           select new { SomeVariable = ....,
                                        AnotherVariable = ....}
                           ).ToList();

      return "TheAnonymousType";
    }
}

5
आप एक अनाम प्रकार क्यों लौटना चाहेंगे? आप संभवतः उस परिणाम का उपयोग कहीं और कैसे कर सकते हैं?
युकर


5
@ हाँ, यदि आप json या कुछ और लौटा रहे हैं जहाँ c # प्रकार कोई फर्क नहीं पड़ता
aw04

10
मुझे नहीं लगता कि यह सवाल तर्क से बाहर है। मुझे वास्तव में कई बार ऐसा करने की आवश्यकता है। इकाई ढांचे का उपयोग करते समय इसकी अधिक स्पष्टता है और आप अपनी क्वेरी एक फ़ंक्शन में करना चाहते हैं और कई स्थानों पर परिणामों का उपयोग करते हैं। स्क्रीन पर परिणाम प्रदर्शित करते समय और फिर रिपोर्ट में या निर्यात करते समय उसी परिणाम का उपयोग करने की आवश्यकता होने पर मुझे इसकी बहुत आवश्यकता होती है। क्वेरी में UI से बहुत सारे फ़िल्टर और जैसे हो सकते हैं। आप वास्तव में कई स्थानों पर एक ही क्वेरी नहीं बनाना चाहते हैं या आप आसानी से सिंक से बाहर निकल सकते हैं जब आप परिणामों में जोड़ना चाहते हैं
केव्बो

जवाबों:


94

आप नहीं कर सकते।

आप केवल लौट सकते हैं object, या वस्तुओं के कंटेनर, जैसे IEnumerable<object>, IList<object>आदि


51
या dynamic। इससे कभी भी काम करना थोड़ा आसान हो जाता है।
vcsjones

आह ठीक है, इसलिए आप केवल एक प्रकार के भीतर अनाम प्रकारों का उपयोग कर सकते हैं लेकिन रिटर्न मानों के रूप में नहीं?
फ्रेंचाई

2
@ फ्रैंकी: हां, केवल सदस्य के शरीर के अंदर। यदि आप इसे वापस करना चाहते हैं - इसे एक प्रसिद्ध प्रकार बनाएं।
अबिशशेव

11
डायनेमिक का उपयोग करना एक समाधान नहीं है, एक गुमनाम प्रकार के क्षेत्र सार्वजनिक नहीं हैं, वे आंतरिक हैं।
हंस पासेंट

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

42

आप वापस आ सकते हैं dynamicजो आपको अनाम प्रकार का रनटाइम चेक किया हुआ संस्करण देगा लेकिन केवल .NET 4+ में


30

C # 7 में हम इसे पूरा करने के लिए tuples का उपयोग कर सकते हैं:

public List<(int SomeVariable, string AnotherVariable)> TheMethod(SomeParameter)
{
  using (MyDC TheDC = new MyDC())
  {
     var TheQueryFromDB = (....
                       select new { SomeVariable = ....,
                                    AnotherVariable = ....}
                       ).ToList();

      return TheQueryFromDB
                .Select(s => (
                     SomeVariable = s.SomeVariable, 
                     AnotherVariable = s.AnotherVariable))
                 .ToList();
  }
}

System.ValueTupleहालांकि आपको नगेट पैकेज स्थापित करने की आवश्यकता हो सकती है ।


27

आप अनाम प्रकार वापस नहीं कर सकते। क्या आप एक मॉडल बना सकते हैं जिसे वापस किया जा सकता है? अन्यथा, आपको एक का उपयोग करना चाहिए object

इस विषय पर जॉन स्कीट द्वारा लिखित एक लेख है

लेख से कोड:

using System;

static class GrottyHacks
{
    internal static T Cast<T>(object target, T example)
    {
        return (T) target;
    }
}

class CheesecakeFactory
{
    static object CreateCheesecake()
    {
        return new { Fruit="Strawberry", Topping="Chocolate" };
    }

    static void Main()
    {
        object weaklyTyped = CreateCheesecake();
        var stronglyTyped = GrottyHacks.Cast(weaklyTyped,
            new { Fruit="", Topping="" });

        Console.WriteLine("Cheesecake: {0} ({1})",
            stronglyTyped.Fruit, stronglyTyped.Topping);            
    }
}

या, यहां एक और समान लेख है

या, जैसा कि अन्य टिप्पणी कर रहे हैं, आप उपयोग कर सकते हैं dynamic


8
निश्चित रूप से मैं एक प्रकार बना सकता हूं; मैं ऐसा करने से बचना चाह रहा था।
फ्रेंचाई

पहला लिंक मर चुका है, क्या आपको पता नहीं होगा कि क्या इसे कहीं और स्थानांतरित किया गया है?
रेमी

17

लौटते समय आवश्यक होने पर आप एक अनाम प्रकार के विकल्प के रूप में टपल क्लास का उपयोग कर सकते हैं:

नोट: टपल में 8 पैरामीटर हो सकते हैं।

return Tuple.Create(variable1, variable2);

या, मूल पद से उदाहरण के लिए:

public List<Tuple<SomeType, AnotherType>> TheMethod(SomeParameter)
{
  using (MyDC TheDC = new MyDC())
  {
     var TheQueryFromDB = (....
                           select Tuple.Create(..., ...)
                           ).ToList();

      return TheQueryFromDB.ToList();
    }
}

http://msdn.microsoft.com/en-us/library/system.tuple(v=vs.110).aspx


10

सी # कंपाइलर दो चरण का कंपाइलर है। पहले चरण में यह सिर्फ नाम स्थान, श्रेणी पदानुक्रम, विधि हस्ताक्षर आदि की जाँच करता है। विधि निकाय दूसरे चरण के दौरान ही संकलित किए जाते हैं।

अनाम प्रकार तब तक निर्धारित नहीं किए जाते हैं जब तक कि विधि शरीर संकलित न हो जाए।

इसलिए कंपाइलर के पास पहले चरण के दौरान वापसी के तरीके को निर्धारित करने का कोई तरीका नहीं है।

यही कारण है कि अनाम प्रकारों का उपयोग रिटर्न प्रकार के रूप में नहीं किया जा सकता है।

जैसा कि दूसरों ने सुझाव दिया है यदि आप .net 4.0 या grater का उपयोग कर रहे हैं, तो आप उपयोग कर सकते हैं Dynamic

अगर मैं तुम होते तो मैं शायद एक टाइप बनाता और उस टाइप को वापस कर देता। इस तरह यह भविष्य के प्रोग्रामर के लिए आसान है जो आपके कोड और अधिक पठनीय को बनाए रखता है।


8

तीन विकल्प:

विकल्प 1:

public class TheRepresentativeType {
    public ... SomeVariable {get;set;}
    public ... AnotherVariable {get;set;}
}

public IEnumerable<TheRepresentativeType> TheMethod(SomeParameter)
{
   using (MyDC TheDC = new MyDC())
   {
     var TheQueryFromDB = (....
                           select new TheRepresentativeType{ SomeVariable = ....,
                                        AnotherVariable = ....}
                           ).ToList();

     return TheQueryFromDB;
   } 
}

विकल्प 2:

public IEnumerable TheMethod(SomeParameter)
{
   using (MyDC TheDC = new MyDC())
   {
     var TheQueryFromDB = (....
                           select new TheRepresentativeType{ SomeVariable = ....,
                                        AnotherVariable = ....}
                           ).ToList();
     return TheQueryFromDB;
   } 
}

आप इसे वस्तु के रूप में प्रसारित कर सकते हैं

विकल्प 3:

public IEnumerable<dynamic> TheMethod(SomeParameter)
{
   using (MyDC TheDC = new MyDC())
   {
     var TheQueryFromDB = (....
                           select new TheRepresentativeType{ SomeVariable = ....,
                                        AnotherVariable = ....}
                           ).ToList();

     return TheQueryFromDB; //You may need to call .Cast<dynamic>(), but I'm not sure
   } 
}

और आप इसे एक गतिशील वस्तु के रूप में वर्गीकृत कर पाएंगे और सीधे उनके गुणों तक पहुंच पाएंगे


3

आप इस मामले में वस्तुओं की सूची वापस कर सकते हैं।

public List<object> TheMethod(SomeParameter)
{
  using (MyDC TheDC = new MyDC())
  {
     var TheQueryFromDB = (....
                           select new { SomeVariable = ....,
                                        AnotherVariable = ....}
                           ).ToList();

      return TheQueryFromDB ;
    }
}

3

C # 7.0 का उपयोग करके हम अभी भी गुमनाम प्रकारों को वापस नहीं कर सकते हैं, लेकिन हमारे पास टुपल प्रकारों का समर्थन है और इस प्रकार हम tuple( System.ValueTuple<T1,T2>इस मामले में) का एक संग्रह लौटा सकते हैं । वर्तमान Tuple types में अभिव्यक्ति पेड़ों में समर्थित नहीं हैं और आपको डेटा को मेमोरी में लोड करने की आवश्यकता है।

कोड का सबसे छोटा संस्करण जो आप चाहते हैं, वह इस तरह दिख सकता है:

public IEnumerable<(int SomeVariable, object AnotherVariable)> TheMethod()
{
    ...

    return (from data in TheDC.Data
        select new { data.SomeInt, data.SomeObject }).ToList()
        .Select(data => (SomeVariable: data.SomeInt, AnotherVariable: data.SomeObject))
}

या धाराप्रवाह Linq सिंटैक्स का उपयोग कर:

return TheDC.Data
    .Select(data => new {SomeVariable: data.SomeInt, AnotherVariable: data.SomeObject})
    .ToList();
    .Select(data => (SomeVariable: data.SomeInt, AnotherVariable: data.SomeObject))

C # 7.1 का उपयोग करके हम टपल के गुणों के नामों को छोड़ सकते हैं और वे ट्यूल इनिशियलाइज़ेशन से प्रभावित होंगे, जैसे कि यह अज्ञात नाम से काम करता है:

select (data.SomeInt, data.SomeObject)
// or
Select(data => (data.SomeInt, data.SomeObject))

2
public List<SomeClass> TheMethod(SomeParameter)
{
  using (MyDC TheDC = new MyDC())
  {
     var TheQueryFromDB = (....
                           select new SomeClass{ SomeVariable = ....,
                                        AnotherVariable = ....}
                           ).ToList();

      return TheQueryFromDB.ToList();
    }
}

public class SomeClass{
   public string SomeVariable{get;set}
   public string AnotherVariable{get;set;}
}

अपनी खुद की कक्षा बनाना और इसके लिए क्वेरी करना सबसे अच्छा समाधान है जो मैं जानता हूं। जैसा कि मैं जानता हूं कि आप अन्य प्रकार में अनाम प्रकार के रिटर्न मानों का उपयोग नहीं कर सकते हैं, क्योंकि यह सिर्फ पहचाना नहीं जाएगा। फिर भी, उनका उपयोग उसी में किया जा सकता है तरीका। मैं उन्हें IQueryableया तो वापस लौटा देता था IEnumerable, हालांकि यह अभी भी आपको यह देखने नहीं देता है कि अनाम प्रकार चर के अंदर क्या है।

मैं कुछ इस तरह से पहले दौड़ता हूं जबकि मैं कुछ कोड को रिफलेक्टर करने की कोशिश कर रहा हूं, आप इसे यहां देख सकते हैं: अलग-अलग तरीकों को फिर से बनाना और बनाना


2

प्रतिबिंब के साथ।

public object tst() {
    var a = new {
        prop1 = "test1",
        prop2 = "test2"
    };

    return a;
}


public string tst2(object anonymousObject, string propName) {
    return anonymousObject.GetType().GetProperties()
        .Where(w => w.Name == propName)
        .Select(s => s.GetValue(anonymousObject))
        .FirstOrDefault().ToString();
}

नमूना:

object a = tst();
var val = tst2(a, "prop2");

आउटपुट:

test2

1

आप केवल डायनामिक कीवर्ड का उपयोग कर सकते हैं,

   dynamic obj = GetAnonymousType();

   Console.WriteLine(obj.Name);
   Console.WriteLine(obj.LastName);
   Console.WriteLine(obj.Age); 


   public static dynamic GetAnonymousType()
   {
       return new { Name = "John", LastName = "Smith", Age=42};
   }

लेकिन डायनामिक टाइप कीवर्ड से आप समय सुरक्षा, आईडीई इंटेलीजेंस आदि को संकलित करेंगे।


0

एक अन्य विकल्प ऑटोमैपर का उपयोग किया जा सकता है: आप अपनी अनाम लौटी हुई वस्तु से किसी भी प्रकार में परिवर्तित हो जाएंगे क्योंकि लंबे समय तक सार्वजनिक गुण मिलान होते हैं। मुख्य बिंदु हैं, रिटर्निंग ऑब्जेक्ट, लिनेक और ऑटोमैपर का उपयोग करें। (या इसी तरह के विचार वापसी क्रमबद्ध जोंस इत्यादि का उपयोग करें या प्रतिबिंब का उपयोग करें ..)

using System.Linq;
using System.Reflection;
using AutoMapper;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;

namespace UnitTestProject1
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
            var data = GetData();

            var firts = data.First();

            var info = firts.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).First(p => p.Name == "Name");
            var value = info.GetValue(firts);

            Assert.AreEqual(value, "One");
        }


        [TestMethod]
        public void TestMethod2()
        {
            var data = GetData();

            var config = new MapperConfiguration(cfg => cfg.CreateMissingTypeMaps = true);
            var mapper = config.CreateMapper();

            var users = data.Select(mapper.Map<User>).ToArray();

            var firts = users.First();

            Assert.AreEqual(firts.Name, "One");

        }

        [TestMethod]
        public void TestMethod3()
        {
            var data = GetJData();


            var users = JsonConvert.DeserializeObject<User[]>(data);

            var firts = users.First();

            Assert.AreEqual(firts.Name, "One");

        }

        private object[] GetData()
        {

            return new[] { new { Id = 1, Name = "One" }, new { Id = 2, Name = "Two" } };
        }

        private string GetJData()
        {

            return JsonConvert.SerializeObject(new []{ new { Id = 1, Name = "One" }, new { Id = 2, Name = "Two" } }, Formatting.None);
        }

        public class User
        {
            public int Id { get; set; }
            public string Name { get; set; }
        }
    }

}

0

अब विशेष रूप से स्थानीय कार्यों के साथ, लेकिन आप हमेशा एक प्रतिनिधि को पास करके कर सकते हैं जो गुमनाम प्रकार बनाता है।

इसलिए यदि आपका लक्ष्य एक ही स्रोतों पर विभिन्न तर्क चलाना है, और परिणामों को एक ही सूची में संयोजित करने में सक्षम है। निश्चित नहीं है कि यह निर्धारित लक्ष्य को पूरा करने के लिए क्या अनुपलब्ध है, लेकिन जब तक आप वापस लौटते हैं Tऔर बनाने के लिए एक प्रतिनिधि को पास करते हैं T, आप एक फ़ंक्शन से एक अनाम प्रकार वापस कर सकते हैं।

// returning an anonymous type
// look mom no casting
void LookMyChildReturnsAnAnonICanConsume()
{
    // if C# had first class functions you could do
    // var anonyFunc = (name:string,id:int) => new {Name=name,Id=id};
    var items = new[] { new { Item1 = "hello", Item2 = 3 } };
    var itemsProjection =items.Select(x => SomeLogic(x.Item1, x.Item2, (y, i) => new { Word = y, Count = i} ));
    // same projection = same type
    var otherSourceProjection = SomeOtherSource((y,i) => new {Word=y,Count=i});
    var q =
        from anony1 in itemsProjection
        join anony2 in otherSourceProjection
            on anony1.Word equals anony2.Word
        select new {anony1.Word,Source1Count=anony1.Count,Source2Count=anony2.Count};
    var togetherForever = itemsProjection.Concat(otherSourceProjection).ToList();
}

T SomeLogic<T>(string item1, int item2, Func<string,int,T> f){
    return f(item1,item2);
}
IEnumerable<T> SomeOtherSource<T>(Func<string,int,T> f){
    var dbValues = new []{Tuple.Create("hello",1), Tuple.Create("bye",2)};
    foreach(var x in dbValues)
        yield return f(x.Item1,x.Item2);
}

0

किसी विशेष उपयोग-मामले में एक विधि से एक अनाम प्रकार को वापस करना वास्तव में संभव है। चलो देखते हैं!

सी # 7 के साथ अनाम प्रकारों को एक विधि से वापस करना संभव है, हालांकि यह थोड़ी सी बाधा के साथ आता है। हम एक नई भाषा सुविधा का उपयोग करने जा रहे हैं जिसे स्थानीय फ़ंक्शन कहते हैं एक अप्रत्यक्ष चाल के साथ (अप्रत्यक्ष की एक और परत किसी भी प्रोग्रामिंग चुनौती को हल कर सकती है?)।

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

आइए देखें कि एक स्थानीय फ़ंक्शन का उपयोग करके इसे कैसे हल किया जाए जो एक अनाम प्रकार बनाता है।

public static HttpClient CreateHttpClient()
{
    // I deal with configuration values in this slightly convoluted way.
    // The benefit is encapsulation of logic and we do not need to
    // create a class, as we can use an anonymous class.
    // The result resembles an expression statement that
    // returns a value (similar to expressions in F#)
    var config = Invoke(() =>
    {
        // slightly complex logic with default value
        // in case of missing configuration value
        // (this is what I want to encapsulate)
        int? acquireTokenTimeoutSeconds = null;
        if (int.TryParse(ConfigurationManager.AppSettings["AcquireTokenTimeoutSeconds"], out int i))
        {
            acquireTokenTimeoutSeconds = i;
        }

        // more complex logic surrounding configuration values ...

        // construct the aggregate configuration class as an anonymous type!
        var c = new
        {
            AcquireTokenTimeoutSeconds =
                acquireTokenTimeoutSeconds ?? DefaultAcquireTokenTimeoutSeconds,
            // ... more properties
        };

        // log the whole object for monitoring purposes
        // (this is also a reason I want encapsulation)
        Log.InfoFormat("Config={0}", c);
        return c;
    });

    // use this configuration in any way necessary...
    // the rest of the method concerns only the factory,
    // i.e. creating the HttpClient with whatever configuration
    // in my case this:
    return new HttpClient(...);

    // local function that enables the above expression
    T Invoke<T>(Func<T> func) => func.Invoke();
}

मैं एक अनाम वर्ग का निर्माण करने में सफल रहा हूं और जटिल सेटिंग्स प्रबंधन से निपटने के तर्क को भी, CreateHttpClientअपने स्वयं के "अभिव्यक्ति" के अंदर और बाहर सभी को समझाया । यह वही नहीं हो सकता है जो ओपी चाहता था लेकिन यह गुमनाम प्रकारों के साथ एक हल्का दृष्टिकोण है जो वर्तमान में आधुनिक सी # में संभव है।

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