Как расширить класс Convert Своими функциями?

303
24 января 2019, 14:00

Собственно допустим у меня есть некоторые классы

public class Model
{
  int id {get;set;}
  string Name {get;set;}
  override string ToString()
     {
        return id+";"+Name;
     }
}
public class LongModel
{
  int id {get;set;}
  string Name {get;set;}
  bool active {get;set;}
   override string ToString()
     {
        return id+";"+Name+";"+active;
     }
}

Сейчас у меня есть класс для конвертирования

public static class ModelConverter
{
  public static Model ToModel(this LongModel LM)
   {
     return new Model{id=LM.id, Name =LM.Name};
   }
    // конвертирование из строки самой модели, конвертирование из строки длинной модели и пр.
}

Можно ли как-то сделать так что-бы эти методы можно было вызывать как Convert.ToModel(longModel) при этом что-бы не пришлось прописывать для каждого случая полный путь:

 System.Convert.Toint32(...);
 Myproject.Convert.ToModel(longModel);

Можно ли вообще такого добиться? N.B. Полный путь приходиться прописывать из-за конфликта имен, по сути вопрос, задан с целю избежать конфликта имен, но использовать всегда одно наименование Convert.

Answer 1

Вы не сможете добавить методы в уже существующий класс Convert, он не спроектирован с учётом возможного расширения. Но можно включить его функциональность в ваш класс. Для этого внесём в Myproject.Convert все методы из System.Convert!

Разумеется, делать это вручную неправильно, нам на помощь придёт кодогенерация. Напишем вспомогательный проект:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
// ...
static string Stringify(Type t) => t.FullName;
static string Stringify(IEnumerable<Type> tl) =>
    string.Join(", ", tl.Select(Stringify));
static string Stringify(ParameterInfo pi)
{
    if (pi.IsIn || pi.ParameterType.IsByRef || pi.HasDefaultValue)
        throw new NotSupportedException();
    return $"{Stringify(pi.ParameterType)} {pi.Name}";
}
static string Stringify(IEnumerable<ParameterInfo> pl) =>
    string.Join(", ", pl.Select(Stringify));
static string StringifyNames(IEnumerable<ParameterInfo> pl) =>
    string.Join(", ", pl.Select(p => p.Name));
static void Main(string[] args)
{
    var methods = typeof(Convert) .GetMethods(BindingFlags.Public | BindingFlags.Static);
    using (var outf = File.CreateText("Convert.proxy.cs"))
    {
        outf.WriteLine("using System.Runtime.CompilerServices;");
        outf.WriteLine();
        outf.WriteLine("namespace MyProject");
        outf.WriteLine("{");
        outf.WriteLine("    static partial class Convert");
        outf.WriteLine("    {");
        outf.WriteLine();
        foreach (var method in methods)
        {
            outf.WriteLine("        [MethodImpl(MethodImplOptions.AggressiveInlining)]");
            outf.Write($"        public static {method.ReturnType.FullName} {method.Name}");
            if (method.IsGenericMethodDefinition)
                outf.Write($"<{Stringify(method.GetGenericArguments())}>");
            var parameters = method.GetParameters();
            outf.WriteLine($"({Stringify(parameters)}) => " +
                $"System.Convert.{method.Name}({StringifyNames(parameters)});");
            outf.WriteLine();
        }
        outf.WriteLine("    }");
        outf.WriteLine("}");
    }
}

Этот код сгенерирует файл Convert.proxy.cs, который вы подключите к основному проекту:

using System.Runtime.CompilerServices;
namespace MyProject
{
    static partial class Convert
    {
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static System.TypeCode GetTypeCode(System.Object value) => System.Convert.GetTypeCode(value);
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static System.Boolean IsDBNull(System.Object value) => System.Convert.IsDBNull(value);

и. т. д.

Поскольку класс объявлен как partial, в него можно добавлять функции, не редактируя сгенерированный текст. Например, дополнительно в другом файле можно написать:

namespace MyProject
{
    static partial class Convert // добавляем методы в класс Convert
    {
        public static Model ToModel(LongModel LM)
        {
            return new Model { id = LM.id, Name = LM.Name };
        }
    }
    // и т. д.
}

Всё, теперь везде можно использовать MyProject.Convert.

Answer 2

Расширять статические классы нельзя.

Однако, в вашем случае, возможно, удобнее будет использовать операторы приведения? Например, у меня есть два класса точек - первый хранит x и y в int, а второй в byte. В любом из этих классов можно объявить операторы приведения к другому классу. Выглядеть это будет так:

public static implicit operator Point2Int(Point2Byte bytePoint)
{
    return new Point2Int(bytePoint.X, bytePoint.Y);
}
public static explicit operator Point2Byte(Point2Int bytePoint)
{
    return new Point2Byte((byte)bytePoint.X, (byte)bytePoint.Y);
}

implicit - говорит, что это приведение будет осуществляться "тихо", т.е. не нужно осуществлять его явно, а explicit, что для него потребуется явное указание необходимости приведения.

Вот пример использования:

//создали новый Point2Int
var pointInt = new Point2Int(1, 1); 
//т.к. приведение к Point2Byte explicit - требуется указать преобразование явно
Point2Byte pointByte = (Point2Byte)pointInt; 
//обратное приведение implicit - поэтому ничего указывать не нужно        
pointInt = pointByte; 

explicit стоит использовать там, где возможна потеря данных (в моём примере int больше байта и при приведении могут потеряться значения, если X, например будет равен 400).
implicit можно использовать, если потеря невозможна (в моём случае byte спокойно превратится в int)

READ ALSO
что означает =&gt; при создании объекта? [дубликат]

что означает => при создании объекта? [дубликат]

На данный вопрос уже ответили:

193
Валидация IDataErrorInfo для свойств, представляющих собой массивы bool[]

Валидация IDataErrorInfo для свойств, представляющих собой массивы bool[]

В окне WPF имеется множество контролов разных типов в перемешкуКонкретнее: анкета из 25 вопросов, в каждом из которых 10-12 вариантов, отраженных...

186
Типизация коллекции объектом Type

Типизация коллекции объектом Type

Есть несколько коллекций:

191