Как осуществить рефакторинг эти общие методы?

ОБНОВЛЕНИЕ: Я должен был упомянуть в исходном сообщении, что хочу узнать больше о дженериках здесь. Я знаю, что это может быть сделано путем изменения базового класса или создания интерфейса это обе реализации классов документов. Но ради этого осуществления я только действительно интересуюсь решениями, которые не требуют никакой модификации к классам документов или их базовому классу. Я думал, что то, что вопрос включает дополнительные методы, подразумевало бы это.

Я записал два почти идентичных универсальных дополнительных метода, и пытаюсь выяснить, как я мог бы осуществить рефакторинг их в отдельный метод. Они отличаются только, в котором воздействует на Список и другой в Списке, и свойствами, которыми я интересуюсь, является AssetID для AssetDocument и PersonID для PersonDocument. Хотя AssetDocument и PersonDocument имеют тот же базовый класс, свойства определяются в каждом классе, таким образом, я не думаю, что это помогает. Я попробовал

public static string ToCSVList<T>(this T list) where T : List<PersonDocument>, List<AssetDocument>

думая я мог бы затем смочь протестировать тип и действовать соответственно, но это приводит к синтаксической ошибке

Параметр типа 'T' наследовал конфликтующие ограничения

Это методы, которые я хотел бы осуществить рефакторинг в отдельный метод, но возможно я просто иду за борт, и их лучше всего оставили бы как они. Я хотел бы услышать то, что Вы думаете.

public static string ToCSVList<T>(this T list) where T : List<AssetDocument>
{
  var sb = new StringBuilder(list.Count * 36 + list.Count);
  string delimiter = String.Empty;

  foreach (var document in list)
  {
    sb.Append(delimiter + document.AssetID.ToString());
    delimiter = ",";
  }

  return sb.ToString();
}

public static string ToCSVList<T>(this T list) where T : List<PersonDocument>
{
  var sb = new StringBuilder(list.Count * 36 + list.Count);
  string delimiter = String.Empty;

  foreach (var document in list)
  {
    sb.Append(delimiter + document.PersonID.ToString());
    delimiter = ",";
  }

  return sb.ToString();
}
5
задан Steve Crane 12 March 2010 в 10:44
поделиться

7 ответов

Ваша реализация в основном перевыполнеет строку. Метод Join, чтобы вы могли попытаться сделать его более простым и универсальным с помощью LINQ:

public static string ToCSVList<T>(this IEnumerable<T> collection)
{ return string.Join(",", collection.Select(x => x.ToString()).ToArray()); }

public static string ToCSVList(this IEnumerable<AssetDocument> assets)
{ return assets.Select(a => a.AssetID).ToCSVList(); }

public static string ToCSVList(this IEnumerable<PersonDocument> persons)
{ return persons.Select(p => p.PersonID).ToCSVList(); }
7
ответ дан 18 December 2019 в 08:27
поделиться

Как вам этот вариант (немного упрощенный, но вы должны уловить идею):

using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main()
        {
            var la = new List<AssetDocument> { new AssetDocument() {AssetID = 1} };

            var result = la.ToCSVList(l => l.AssetID.ToString());
        }
    }

    public class AssetDocument
    {
        public int AssetID { get; set; }
    }

    public static class GlobalExtensions
    {
        public static string ToCSVList<T>(this List<T> list, Func<T, string> propResolver)
        {
            var sb = new StringBuilder(list.Count * 36 + list.Count);
            var delimiter = "";

            foreach (var document in list)
            {
                sb.Append(delimiter);
                sb.Append(propResolver(document));
                delimiter = ",";
            }

            return sb.ToString();
        }
    }
}

Это будет работать с любым списком (в случае, если вас не волнует предварительно выделенная память в StringBuilder даже с любым IEnumerable ).

Обновление: даже если вы хотите сохранить исходные методы расширения, вы можете сократить их до одной строчки кода.

2
ответ дан 18 December 2019 в 08:27
поделиться

Я знаю только java, поэтому я не могу указать правильный синтаксис, но общий подход должен работать:

определить интерфейс Document, который будет реализован от PersonDocument и AssetDocument, с методом

String getIdString();

Используйте список в качестве параметра вашего метода. Обратите внимание, что это синтаксис java для списка чего-то, что наследуется / расширяется от документа.

1
ответ дан 18 December 2019 в 08:27
поделиться

Я думаю, что можно было бы разрешить PersonDocument и AssetDocument наследовать класс Document, у которого будет свойство Id, в котором хранится ваш текущий PersonId или AssetId соответственно.

3
ответ дан 18 December 2019 в 08:27
поделиться

Создайте абстракцию, например IDocument или абстрактный класс BaseDocument , который предоставляет идентификатор (который является только поле, которое вы действительно используете) и сделайте так, чтобы оба PersonDocument и AssetDocument реализовали это. Теперь пусть ваш универсальный метод принимает вместо него IDocument или BaseDocument .

3
ответ дан 18 December 2019 в 08:27
поделиться

А как насчет того, чтобы ваш метод также принимал делегата для возврата document.AssetID.ToString () для этого списка?

При использовании Lamda-выражений это могло бы быть достаточно легким, хотя и немного некрасивым. Консольное приложение для демонстрации:

    class Program
    {
    static void Main(string[] args)
    {
        List<string> strings = new List<string> { "hello", "world", "this", "is", "my", "list" };
        List<DateTime> dates = new List<DateTime> { DateTime.Now, DateTime.MinValue, DateTime.MaxValue };

        Console.WriteLine(ToCSVList(strings, (string s) => { return s.Length.ToString(); }));
        Console.WriteLine(ToCSVList(dates, (DateTime d) => { return d.ToString(); }));

        Console.ReadLine();
    }

    public static string ToCSVList<T, U>(T list, Func<U, String> f) where T : IList<U>
    {
        var sb = new StringBuilder(list.Count * 36 + list.Count);
        string delimiter = String.Empty;

        foreach (var document in list)
        {
            sb.Append(delimiter + f(document));
            delimiter = ",";
        }

        return sb.ToString();
    }
}

Независимо от того, лучший это подход или нет, я оставляю читателю в качестве упражнения!

2
ответ дан 18 December 2019 в 08:27
поделиться

Отражение можно использовать для небольшого действия Duck Typing !

Я предположил, что ваши классы называются # class # Document, и вы хотите объединить свойства # class # ID. Если список содержит классы, соответствующие этому наименованию, они будут объединены. В противном случае они этого не сделают.

Именно так работает фреймворк Rails , используя соглашение по конфигурации .

Очевидно, такое поведение больше подходит для динамических языков, таких как Ruby. Вероятно, лучшим решением для более статичного языка, такого как C #, было бы рефакторинг базовых классов, использование интерфейсов и т. Д. Но этого не было в спецификации, и для образовательных целей это один из способов решения проблемы!

public static class Extensions
{
    public static string ToCSVList<T> ( this T list ) where T : IList
    {
        var sb = new StringBuilder ( list.Count * 36 + list.Count );
        string delimiter = String.Empty;

        foreach ( var document in list )
        {
            string propertyName = document.GetType ().Name.Replace("Document", "ID");
            PropertyInfo property = document.GetType ().GetProperty ( propertyName );
            if ( property != null )
            {
                string value = property.GetValue ( document, null ).ToString ();

                sb.Append ( delimiter + value );
                delimiter = ",";
            }
        }

        return sb.ToString ();
    }
}

Использование (обратите внимание, нет необходимости в наследовании с Duck Typing - также работает с любым типом!):

public class GroovyDocument
{
    public string GroovyID
    {
        get;
        set;
    }
}

public class AssetDocument
{
    public int AssetID
    {
        get;
        set;
    }
}

...

        List<AssetDocument> docs = new List<AssetDocument> ();
        docs.Add ( new AssetDocument () { AssetID = 3 } );
        docs.Add ( new AssetDocument () { AssetID = 8 } );
        docs.Add ( new AssetDocument () { AssetID = 10 } );

        MessageBox.Show ( docs.ToCSVList () );

        List<GroovyDocument> rocs = new List<GroovyDocument> ();
        rocs.Add ( new GroovyDocument () { GroovyID = "yay" } );
        rocs.Add ( new GroovyDocument () { GroovyID = "boo" } );
        rocs.Add ( new GroovyDocument () { GroovyID = "hurrah" } );

        MessageBox.Show ( rocs.ToCSVList () );

...

1
ответ дан 18 December 2019 в 08:27
поделиться
Другие вопросы по тегам:

Похожие вопросы: