Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write extension methods for anonymous types?

I'm trying to create a CSV extension method for my enumerable list and I'm stumped. Here's how I created my simple enumerated list:

var CAquery = from temp in CAtemp
 join casect in CAdb.sectors
 on temp.sector_code equals casect.sector_code
 select new
 {       
     CUSIP = temp.equity_cusip,
     CompName = temp.company_name,
     Exchange = temp.primary_exchange       
 };

CAquery.WriteToCSVFile();

This is what I have done so far in creating an extension method (which I think is wrong):

public static class CSVExtensions
{        
    public static void WriteToCSVFile(this IEnumerable<T> myList)
    {

Do you see what I'm doing wrong?

like image 814
inquisitive_one Avatar asked Jan 30 '12 17:01

inquisitive_one


People also ask

How do you declare an extension method?

An extension method must be defined in a top-level static class. An extension method with the same name and signature as an instance method will not be called. Extension methods cannot be used to override existing methods. The concept of extension methods cannot be applied to fields, properties or events.

Can extension methods access private methods?

Extension methods cannot access private variables in the type they are extending.

How do you define anonymous type?

Anonymous types are class types that derive directly from object , and that cannot be cast to any type except object . The compiler provides a name for each anonymous type, although your application cannot access it.

Do anonymous types work with LINQ?

You are allowed to use an anonymous type in LINQ. In LINQ, select clause generates anonymous type so that in a query you can include properties that are not defined in the class.


Video Answer


2 Answers

You have to specify the generic type parameter in the method signature:

public static class CSVExtensions
{        
    public static void WriteToCSVFile<T>(this IEnumerable<T> myList)
    {
       //your code here
    }
}

Are you truly trying to write an extension method that should work on any IEnumerable<T> or is your type more specific? If the later is the case you should replace T with the type you want to support (or add sufficient constraints).

Edit:

In light of comments - you should project to a class instead of an anonymous type in your query - then you can use an extension method for this particular type, i.e.:

class CompanyTicker
{
  public string CUSIP {get;set;}
  public string CompName  {get;set;}
  public string Exchange {get;set;}
}

Now your query can be:

var CAquery = from temp in CAtemp
 join casect in CAdb.sectors
 on temp.sector_code equals casect.sector_code
 select new CompanyTicker
 {       
     CUSIP = temp.equity_cusip,
     CompName = temp.company_name,
     Exchange = temp.primary_exchange       
 };

And your extension method (which now doesn't need to be generic) becomes:

public static class CSVExtensions
{        
    public static void WriteToCSVFile(this IEnumerable<CompanyTicker> myList)
    {
       //your code here
    }
}
like image 101
BrokenGlass Avatar answered Sep 16 '22 20:09

BrokenGlass


It is possible to do what you are trying to do using reflection. The performance is going to be somewhat worse than if you write non-generic code however.

Here is a complete code sample:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;

class Program
{
    static void Main(string[] args)
    {
        var seq =
        Enumerable.Range(0, 100)
            .Select(i => new { Name = "Item" + i, Value = i })
            ;

        seq.WriteCsv(Console.Out);
        Console.ReadLine();
    }
}

public static class CsvExtension
{

    public static void WriteCsv<T>(this IEnumerable<T> seq, TextWriter writer)
    {
        var type = typeof(T);

        MethodInfo[] getters = type.GetProperties().Select(pi => pi.GetGetMethod()).ToArray();


        // only supporting simple properties
        // indexer properties will probably fail
        var args = new object[0];

        foreach (var item in seq)
        {
            for (int i = 0; i < getters.Length; i++)
            {
                if (i != 0)
                    writer.Write(",");

                Object value = getters[i].Invoke(item, args);
                var str = value.ToString();

                if (str.Contains(",") || str.Contains("\""))
                {
                    var escaped = str.Replace("\"", "\\\"");
                    writer.Write("\"");
                    writer.Write(escaped);
                    writer.Write("\"");
                }
                else
                {
                    writer.Write(str);
                }
            }


            writer.WriteLine();
        }
    }
}
like image 43
MarkPflug Avatar answered Sep 18 '22 20:09

MarkPflug