การแปลงรายการทั่วไปเป็นสตริง CSV


142

ฉันมีรายการค่าจำนวนเต็ม (รายการ) และต้องการสร้างสตริงของค่าที่คั่นด้วยลูกน้ำ นั่นคือรายการทั้งหมดในผลลัพธ์รายการไปยังรายการคั่นด้วยเครื่องหมายจุลภาคเดียว

ความคิดของฉัน ... 1. ส่งรายการไปยังวิธีการ 2. ใช้ stringbuilder เพื่อย้ำรายการและต่อท้ายจุลภาค 3. ทดสอบอักขระสุดท้ายและถ้าเป็นจุลภาคให้ลบออก

คุณคิดยังไง? วิธีนี้เป็นวิธีที่ดีที่สุดหรือไม่?

รหัสของฉันจะเปลี่ยนไปอย่างไรหากฉันต้องการจัดการไม่เพียง แต่จำนวนเต็ม (แผนปัจจุบันของฉัน) แต่ยังรวมถึงสตริง, longs, doubles, bools ฯลฯ ในอนาคต ฉันเดาว่ายอมรับรายการประเภทใดก็ได้

คำตอบ:


250

มันน่าทึ่งมากที่ Framework ทำเพื่อเราแล้ว

List<int> myValues;
string csv = String.Join(",", myValues.Select(x => x.ToString()).ToArray());

สำหรับกรณีทั่วไป:

IEnumerable<T> myList;
string csv = String.Join(",", myList.Select(x => x.ToString()).ToArray());

อย่างที่คุณเห็นมันมีประสิทธิภาพไม่แตกต่างกัน ระวังว่าคุณอาจต้องใส่x.ToString()เครื่องหมายอัญประกาศ (เช่น"\"" + x.ToString() + "\"") ในกรณีที่x.ToString()มีเครื่องหมายจุลภาค

สำหรับการอ่านที่น่าสนใจเกี่ยวกับรูปแบบเล็กน้อยของสิ่งนี้: ดูComma Quibblingในบล็อกของ Eric Lippert

หมายเหตุ: สิ่งนี้ถูกเขียนขึ้นก่อน. NET 4.0 อย่างเป็นทางการ ตอนนี้เราสามารถพูดได้

IEnumerable<T> sequence;
string csv = String.Join(",", sequence);

String.Join<T>(string, IEnumerable<T>)ใช้เกินพิกัด วิธีการนี้จะฉายแต่ละองค์ประกอบจะxx.ToString()


List<int>ไม่มีวิธีการSelectในกรอบ 3.5 เว้นแต่ฉันจะพลาดบางอย่าง
ajeh

2
@ajeh: คุณอาจจะพลาดusingคำสั่ง
สัน

นำเข้าเฉพาะใด
ajeh

1
ลองSystem.Linq.Enumerable(และแน่นอนคุณจะต้องมีการSystem.Core.dllประกอบ แต่น่าจะมีอยู่แล้ว) คุณจะเห็นว่าList<int> ไม่เคยมีSelectวิธีการ แต่System.Linq.EnumerableกำหนดSelectเป็นวิธีการขยายIEnumerable<T>ซึ่งList<int>เป็นตัวอย่างของ. ดังนั้นคุณต้องใช้System.Linq.Enumerableในการนำเข้าเพื่อเลือกวิธีการขยายนี้
สัน

1
คำตอบของคุณไม่ได้ผลสำหรับฉัน ฉันได้รับสตริงคั่นด้วยจุลภาคเพียงบรรทัดเดียวซึ่งมีเฉพาะชื่อประเภทของรายการทั่วไป <T>
Thameem

15

ใน 3.5 ฉันยังทำได้ มันง่ายกว่ามากและไม่จำเป็นต้องใช้แลมด้า

String.Join(",", myList.ToArray<string>());

ToArray()วิธีการList<int>ไม่สามารถใช้กับอาร์กิวเมนต์ประเภทในกรอบงาน 3.5 เว้นแต่ฉันจะขาดบางอย่าง
ajeh

1
ยอดเยี่ยม. ไม่จำเป็นต้องใช้ ToArray <string> เนื่องจากใช้ ToString () ลูก
Christian

11

คุณสามารถสร้างวิธีการขยายที่คุณสามารถเรียกใช้ IEnumerable:

public static string JoinStrings<T>(
    this IEnumerable<T> values, string separator)
{
    var stringValues = values.Select(item =>
        (item == null ? string.Empty : item.ToString()));
    return string.Join(separator, stringValues.ToArray());
}

จากนั้นคุณสามารถเรียกใช้เมธอดในรายการเดิม:

string commaSeparated = myList.JoinStrings(", ");

7

คุณสามารถใช้String.Join.

String.Join(
  ",",
  Array.ConvertAll(
     list.ToArray(),
     element => element.ToString()
  )
);

ไม่จำเป็นต้องระบุพารามิเตอร์ประเภททั่วไปในการเรียกConvertAllที่นี่ - ทั้งคู่intและstringจะถูกอนุมาน
Pavel Minaev

1
แทนที่จะทำArray.ConvertAll(...' you can just do list.ConvertAll (e => e ToString ()). ToArray) `เพียงแค่พิมพ์น้อยลง
เดวิด

string.Join (",", รายการ); จะทำได้ดี :)
Christian

6

หากเนื้อหาใดต้องการแปลงรายการของคลาสออบเจ็กต์แบบกำหนดเองแทนรายการสตริงให้แทนที่เมธอด ToString ของคลาสของคุณด้วยการแสดงแถว csv ของคลาสของคุณ

Public Class MyClass{
   public int Id{get;set;}
   public String PropertyA{get;set;}
   public override string ToString()
   {
     return this.Id+ "," + this.PropertyA;
   }
}

จากนั้นสามารถใช้โค้ดต่อไปนี้เพื่อแปลงรายการคลาสนี้เป็น CSV พร้อมคอลัมน์ส่วนหัว

string csvHeaderRow = String.Join(",", typeof(MyClass).GetProperties(BindingFlags.Public | BindingFlags.Instance).Select(x => x.Name).ToArray<string>()) + Environment.NewLine;
string csv= csvHeaderRow + String.Join(Environment.NewLine, MyClass.Select(x => x.ToString()).ToArray());

myExampleCollection เลือกแทน MyClass เลือก
Piotr Ferenc

6

ฉันอธิบายในเชิงลึกในโพสต์นี้นี้ ฉันจะวางโค้ดที่นี่พร้อมคำอธิบายสั้น ๆ

นี่คือวิธีการสร้างแถวส่วนหัว ใช้ชื่อคุณสมบัติเป็นชื่อคอลัมน์

private static void CreateHeader<T>(List<T> list, StreamWriter sw)
    {
        PropertyInfo[] properties = typeof(T).GetProperties();
        for (int i = 0; i < properties.Length - 1; i++)
        {
            sw.Write(properties[i].Name + ",");
        }
        var lastProp = properties[properties.Length - 1].Name;
        sw.Write(lastProp + sw.NewLine);
    }

วิธีนี้จะสร้างแถวค่าทั้งหมด

private static void CreateRows<T>(List<T> list, StreamWriter sw)
    {
        foreach (var item in list)
        {
            PropertyInfo[] properties = typeof(T).GetProperties();
            for (int i = 0; i < properties.Length - 1; i++)
            {
                var prop = properties[i];
                sw.Write(prop.GetValue(item) + ",");
            }
            var lastProp = properties[properties.Length - 1];
            sw.Write(lastProp.GetValue(item) + sw.NewLine);
        }
    }

และนี่คือวิธีการที่รวบรวมและสร้างไฟล์จริง

public static void CreateCSV<T>(List<T> list, string filePath)
    {
        using (StreamWriter sw = new StreamWriter(filePath))
        {
            CreateHeader(list, sw);
            CreateRows(list, sw);
        }
    }

1
นี้ได้ผลดีมาก ฉันปรับปรุงสิ่งนี้เพื่อส่งผ่านตัวคั่นเป็นพารามิเตอร์ดังนั้นจึงสามารถสร้างไฟล์ที่ใช้ตัวคั่นประเภทใดก็ได้ CSV เป็นเรื่องยากที่จะจัดการหากข้อความมีเครื่องหมายจุลภาคดังนั้นฉันจึงสร้าง|ไฟล์ที่มีตัวคั่นโดยใช้เวอร์ชันที่ปรับปรุงแล้ว ขอบคุณ!
Shiva

5

เนื่องจากรหัสในลิงค์ที่กำหนดโดย @Frank สร้างไฟล์ CSV จาก. NET Generic Listมีปัญหาเล็กน้อยในการสิ้นสุดทุกบรรทัดโดยที่,ฉันแก้ไขรหัสเพื่อกำจัดมันหวังว่าจะช่วยใครบางคน

/// <summary>
/// Creates the CSV from a generic list.
/// </summary>;
/// <typeparam name="T"></typeparam>;
/// <param name="list">The list.</param>;
/// <param name="csvNameWithExt">Name of CSV (w/ path) w/ file ext.</param>;
public static void CreateCSVFromGenericList<T>(List<T> list, string csvCompletePath)
{
    if (list == null || list.Count == 0) return;

    //get type from 0th member
    Type t = list[0].GetType();
    string newLine = Environment.NewLine;

    if (!Directory.Exists(Path.GetDirectoryName(csvCompletePath))) Directory.CreateDirectory(Path.GetDirectoryName(csvCompletePath));

    if (!File.Exists(csvCompletePath)) File.Create(csvCompletePath);

    using (var sw = new StreamWriter(csvCompletePath))
    {
        //make a new instance of the class name we figured out to get its props
        object o = Activator.CreateInstance(t);
        //gets all properties
        PropertyInfo[] props = o.GetType().GetProperties();

        //foreach of the properties in class above, write out properties
        //this is the header row
        sw.Write(string.Join(",", props.Select(d => d.Name).ToArray()) + newLine);

        //this acts as datarow
        foreach (T item in list)
        {
            //this acts as datacolumn
            var row = string.Join(",", props.Select(d => item.GetType()
                                                            .GetProperty(d.Name)
                                                            .GetValue(item, null)
                                                            .ToString())
                                                    .ToArray());
            sw.Write(row + newLine);

        }
    }
}

ข้อมูลเพิ่มเติม: กระบวนการนี้ไม่สามารถเข้าถึงไฟล์ 'c: \ temp \ matchingMainWav.csv' ได้เนื่องจากถูกใช้โดยกระบวนการอื่น มีโฟลเดอร์devอยู่ แต่ไม่ใช่ไฟล์ ... ฉันไม่ได้ใช้งานใช่ไหม
Tom Stickel

เมธอด File.Create สร้างไฟล์และเปิด FileStream บนไฟล์ ดังนั้นไฟล์ของคุณจึงเปิดอยู่แล้ว คุณไม่ต้องการไฟล์จริงๆสร้างวิธีการเลย:
David

หากคุณสมบัติใด ๆ เป็นโมฆะมีวิธีแก้ปัญหานั้นหรือไม่?
Daniel Jackson

@DanielJackson คุณสามารถเขียน where clause ในข้อความนี้sw.Write(string.Join(",", props.Select(d => d.Name).ToArray()) + newLine);ไม่ได้ทดสอบ แต่ไม่รู้ว่าคุณพยายามทำอะไร
Ali Umair

4

ฉันชอบวิธีการขยายที่เรียบง่ายดี

 public static string ToCsv(this List<string> itemList)
         {
             return string.Join(",", itemList);
         }

จากนั้นคุณสามารถเรียกใช้เมธอดในรายการเดิม:

string CsvString = myList.ToCsv();

สะอาดและอ่านง่ายกว่าคำแนะนำอื่น ๆ


3

โซลูชันใด ๆ จะใช้ได้เฉพาะเมื่อ List a list (of string)

หากคุณมีรายการทั่วไปของออบเจ็กต์ของคุณเองเช่นรายการ (ของรถยนต์) ที่รถมีคุณสมบัติ n คุณต้องวนซ้ำ PropertiesInfo ของแต่ละออบเจ็กต์รถ

ดูที่: http://www.csharptocsharp.com/generate-csv-from-generic-list


1
คุณไม่สามารถแทนที่ ToString ของคลาสและใช้วิธีการด้านบนได้หรือไม่?
Gabriel Guimarães

3

ห้องสมุด CsvHelper เป็นที่นิยมมากใน Nuget คุณคุ้มค่าผู้ชาย! https://github.com/JoshClose/CsvHelper/wiki/Basics

การใช้ CsvHelper นั้นง่ายมาก การตั้งค่าเริ่มต้นเป็นการตั้งค่าสำหรับสถานการณ์ที่พบบ่อยที่สุด

นี่คือข้อมูลการตั้งค่าเล็กน้อย

Actors.csv:

Id,FirstName,LastName  
1,Arnold,Schwarzenegger  
2,Matt,Damon  
3,Christian,Bale

Actor.cs (อ็อบเจ็กต์คลาสแบบกำหนดเองที่แสดงถึงนักแสดง):

public class Actor
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

การอ่านไฟล์ CSV โดยใช้ CsvReader:

var csv = new CsvReader( new StreamReader( "Actors.csv" ) );

var ActorsList = csv.GetRecords ();

กำลังเขียนไฟล์ CSV

using (var csv = new CsvWriter( new StreamWriter( "Actors.csv" ) )) 
{
    csv.WriteRecords( actorsList );
}

2

ปัญหาเกี่ยวกับ String.Join คือคุณไม่ได้จัดการกรณีของเครื่องหมายจุลภาคที่มีอยู่แล้วในค่า เมื่อมีเครื่องหมายจุลภาคแล้วคุณจะล้อมรอบค่าในเครื่องหมายคำพูดและแทนที่คำพูดที่มีอยู่ทั้งหมดด้วยเครื่องหมายคำพูดคู่

String.Join(",",{"this value has a , in it","This one doesn't", "This one , does"});

ดูโมดูล CSV


2

ไม่ว่าจะด้วยเหตุผลใดก็ตาม @AliUmair ได้เปลี่ยนการแก้ไขกลับเป็นคำตอบของเขาที่แก้ไขโค้ดของเขาที่ไม่ทำงานตามที่เป็นอยู่ดังนั้นนี่คือเวอร์ชันที่ใช้งานได้ซึ่งไม่มีข้อผิดพลาดในการเข้าถึงไฟล์และจัดการค่าคุณสมบัติของวัตถุว่างอย่างถูกต้อง:

/// <summary>
/// Creates the CSV from a generic list.
/// </summary>;
/// <typeparam name="T"></typeparam>;
/// <param name="list">The list.</param>;
/// <param name="csvNameWithExt">Name of CSV (w/ path) w/ file ext.</param>;
public static void CreateCSVFromGenericList<T>(List<T> list, string csvCompletePath)
{
    if (list == null || list.Count == 0) return;

    //get type from 0th member
    Type t = list[0].GetType();
    string newLine = Environment.NewLine;

    if (!Directory.Exists(Path.GetDirectoryName(csvCompletePath))) Directory.CreateDirectory(Path.GetDirectoryName(csvCompletePath));

    using (var sw = new StreamWriter(csvCompletePath))
    {
        //make a new instance of the class name we figured out to get its props
        object o = Activator.CreateInstance(t);
        //gets all properties
        PropertyInfo[] props = o.GetType().GetProperties();

        //foreach of the properties in class above, write out properties
        //this is the header row
        sw.Write(string.Join(",", props.Select(d => d.Name).ToArray()) + newLine);

        //this acts as datarow
        foreach (T item in list)
        {
            //this acts as datacolumn
            var row = string.Join(",", props.Select(d => $"\"{item.GetType().GetProperty(d.Name).GetValue(item, null)?.ToString()}\"")
                                                    .ToArray());
            sw.Write(row + newLine);

        }
    }
}

1

http://cc.davelozinski.com/c-sharp/the-fastest-way-to-read-and-process-text-files

เว็บไซต์นี้ทำการทดสอบอย่างละเอียดเกี่ยวกับวิธีการเขียนไฟล์โดยใช้ตัวเขียนบัฟเฟอร์การอ่านทีละบรรทัดดูเหมือนจะเป็นวิธีที่ดีที่สุดการใช้ตัวสร้างสตริงเป็นวิธีที่ช้าที่สุด

ฉันใช้เทคนิคของเขาเป็นอย่างมากในการเขียนสิ่งต่าง ๆ เพื่อให้ไฟล์ทำงานได้ดี


1

วิธีการขยาย ToCsv () วัตถุประสงค์ทั่วไป:

  • รองรับ Int16 / 32/64, float, double, decimal และอะไรก็ได้ที่รองรับ ToString ()
  • ตัวคั่นการรวมแบบกำหนดเองที่เป็นทางเลือก
  • ตัวเลือกแบบกำหนดเองเพิ่มเติม
  • ข้อมูลจำเพาะการจัดการที่เป็นโมฆะ / ว่างเปล่า (* Opt () เกินพิกัด)

ตัวอย่างการใช้งาน:

"123".ToCsv() // "1,2,3"
"123".ToCsv(", ") // "1, 2, 3"
new List<int> { 1, 2, 3 }.ToCsv() // "1,2,3"

new List<Tuple<int, string>> 
{ 
    Tuple.Create(1, "One"), 
    Tuple.Create(2, "Two") 
}
.ToCsv(t => t.Item2);  // "One,Two"

((string)null).ToCsv() // throws exception
((string)null).ToCsvOpt() // ""
((string)null).ToCsvOpt(ReturnNullCsv.WhenNull) // null

การนำไปใช้

/// <summary>
/// Specifies when ToCsv() should return null.  Refer to ToCsv() for IEnumerable[T]
/// </summary>
public enum ReturnNullCsv
{
    /// <summary>
    /// Return String.Empty when the input list is null or empty.
    /// </summary>
    Never,

    /// <summary>
    /// Return null only if input list is null.  Return String.Empty if list is empty.
    /// </summary>
    WhenNull,

    /// <summary>
    /// Return null when the input list is null or empty
    /// </summary>
    WhenNullOrEmpty,

    /// <summary>
    /// Throw if the argument is null
    /// </summary>
    ThrowIfNull
}   

/// <summary>
/// Converts IEnumerable list of values to a comma separated string values.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="values">The values.</param>        
/// <param name="joinSeparator"></param>
/// <returns>System.String.</returns>
public static string ToCsv<T>(
    this IEnumerable<T> values,            
    string joinSeparator = ",")
{
    return ToCsvOpt<T>(values, null /*selector*/, ReturnNullCsv.ThrowIfNull, joinSeparator);
}

/// <summary>
/// Converts IEnumerable list of values to a comma separated string values.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="values">The values.</param>
/// <param name="selector">An optional selector</param>
/// <param name="joinSeparator"></param>
/// <returns>System.String.</returns>
public static string ToCsv<T>(
    this IEnumerable<T> values,
    Func<T, string> selector,            
    string joinSeparator = ",") 
{
    return ToCsvOpt<T>(values, selector, ReturnNullCsv.ThrowIfNull, joinSeparator);
}

/// <summary>
/// Converts IEnumerable list of values to a comma separated string values.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="values">The values.</param>
/// <param name="returnNullCsv">Return mode (refer to enum ReturnNullCsv).</param>
/// <param name="joinSeparator"></param>
/// <returns>System.String.</returns>
public static string ToCsvOpt<T>(
    this IEnumerable<T> values,
    ReturnNullCsv returnNullCsv = ReturnNullCsv.Never,
    string joinSeparator = ",")
{
    return ToCsvOpt<T>(values, null /*selector*/, returnNullCsv, joinSeparator);
}

/// <summary>
/// Converts IEnumerable list of values to a comma separated string values.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="values">The values.</param>
/// <param name="selector">An optional selector</param>
/// <param name="returnNullCsv">Return mode (refer to enum ReturnNullCsv).</param>
/// <param name="joinSeparator"></param>
/// <returns>System.String.</returns>
public static string ToCsvOpt<T>(
    this IEnumerable<T> values, 
    Func<T, string> selector,
    ReturnNullCsv returnNullCsv = ReturnNullCsv.Never,
    string joinSeparator = ",")
{
    switch (returnNullCsv)
    {
        case ReturnNullCsv.Never:
            if (!values.AnyOpt())
                return string.Empty;
            break;

        case ReturnNullCsv.WhenNull:
            if (values == null)
                return null;
            break;

        case ReturnNullCsv.WhenNullOrEmpty:
            if (!values.AnyOpt())
                return null;
            break;

        case ReturnNullCsv.ThrowIfNull:
            if (values == null)
                throw new ArgumentOutOfRangeException("ToCsvOpt was passed a null value with ReturnNullCsv = ThrowIfNull.");
            break;

        default:
            throw new ArgumentOutOfRangeException("returnNullCsv", returnNullCsv, "Out of range.");
    }

    if (selector == null)
    {
        if (typeof(T) == typeof(Int16) || 
            typeof(T) == typeof(Int32) || 
            typeof(T) == typeof(Int64))
        {                   
            selector = (v) => Convert.ToInt64(v).ToStringInvariant();
        }
        else if (typeof(T) == typeof(decimal))
        {
            selector = (v) => Convert.ToDecimal(v).ToStringInvariant();
        }
        else if (typeof(T) == typeof(float) ||
                typeof(T) == typeof(double))
        {
            selector = (v) => Convert.ToDouble(v).ToString(CultureInfo.InvariantCulture);
        }
        else
        {
            selector = (v) => v.ToString();
        }            
    }

    return String.Join(joinSeparator, values.Select(v => selector(v)));
}

public static string ToStringInvariantOpt(this Decimal? d)
{
    return d.HasValue ? d.Value.ToStringInvariant() : null;
}

public static string ToStringInvariant(this Decimal d)
{
    return d.ToString(CultureInfo.InvariantCulture);
}

public static string ToStringInvariantOpt(this Int64? l)
{
    return l.HasValue ? l.Value.ToStringInvariant() : null;
}

public static string ToStringInvariant(this Int64 l)
{
    return l.ToString(CultureInfo.InvariantCulture);
}

public static string ToStringInvariantOpt(this Int32? i)
{
    return i.HasValue ? i.Value.ToStringInvariant() : null;
}

public static string ToStringInvariant(this Int32 i)
{
    return i.ToString(CultureInfo.InvariantCulture);
}

public static string ToStringInvariantOpt(this Int16? i)
{
    return i.HasValue ? i.Value.ToStringInvariant() : null;
}

public static string ToStringInvariant(this Int16 i)
{
    return i.ToString(CultureInfo.InvariantCulture);
}

1

นี่คือวิธีการขยายของฉันมันส่งคืนสตริงเพื่อความเรียบง่าย แต่การใช้งานของฉันเขียนไฟล์ไปยัง data lake

มันมีตัวคั่นใด ๆ เพิ่มเครื่องหมายคำพูดลงในสตริง (ในกรณีที่มีตัวคั่น) และดีลจะเป็นโมฆะ

    /// <summary>
    /// A class to hold extension methods for C# Lists 
    /// </summary>
    public static class ListExtensions
    {
        /// <summary>
        /// Convert a list of Type T to a CSV
        /// </summary>
        /// <typeparam name="T">The type of the object held in the list</typeparam>
        /// <param name="items">The list of items to process</param>
        /// <param name="delimiter">Specify the delimiter, default is ,</param>
        /// <returns></returns>
        public static string ToCsv<T>(this List<T> items, string delimiter = ",")
        {
            Type itemType = typeof(T);
            var props = itemType.GetProperties(BindingFlags.Public | BindingFlags.Instance).OrderBy(p => p.Name);

            var csv = new StringBuilder();

            // Write Headers
            csv.AppendLine(string.Join(delimiter, props.Select(p => p.Name)));

            // Write Rows
            foreach (var item in items)
            {
                // Write Fields
                csv.AppendLine(string.Join(delimiter, props.Select(p => GetCsvFieldasedOnValue(p, item))));
            }

            return csv.ToString();
        }

        /// <summary>
        /// Provide generic and specific handling of fields
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="p"></param>
        /// <param name="item"></param>
        /// <returns></returns>
        private static object GetCsvFieldasedOnValue<T>(PropertyInfo p, T item)
        {
            string value = "";

            try
            {
                value = p.GetValue(item, null)?.ToString();
                if (value == null) return "NULL";  // Deal with nulls
                if (value.Trim().Length == 0) return ""; // Deal with spaces and blanks

                // Guard strings with "s, they may contain the delimiter!
                if (p.PropertyType == typeof(string))
                {
                    value = string.Format("\"{0}\"", value);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return value;
        }
    }

การใช้งาน:

 // Tab Delimited (TSV)
 var csv = MyList.ToCsv<MyClass>("\t");
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.