Подтвердить что ты не робот

Существует ли более эффективный способ определения аналогичных общедоступных свойств

У меня есть класс с почти 20 общедоступными свойствами. Эти свойства имеют общее значение, что все они являются строками, и они заполнены данными из разных таблиц базы данных.

Кроме того, набор довольно нормальный, в то время как get является особенным, поскольку мне нужно вызвать конкретный метод. Это делается для каждого свойства в данный момент (см. Ниже).

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

class myclass
{
     private string _Firstname;
     private string _Lastname;
     .....
     public string Firstname
     {
         get {
             return ModifyStringMethod(this._Firstname);
         }

         set {
             this._Firstname = value;
         }
     }
}

Как упоминалось выше, каждое государственное имущество выглядит одинаково. Получает вызовы ModifyStringMethod с частным членом, заданным как параметр, в то время как набор просто устанавливает частный член.

4b9b3361

Ответ 1

Вы можете попробовать автоматическое создание кода с помощью T4 template. Они идеальны, когда у вас есть простой, повторяющийся образец кода, и вы не ожидаете, что некоторые случаи будут немного отличаться от других.

Просто определите XML со списком имен свойств и создайте шаблон T4 для частичного класса с каждым свойством.

Ответ 2

Другая опция похожа на решение Dion V., но использует неявное преобразование, чтобы заставить поведение вести себя как обычная строка извне и позволяет использовать простое автоматическое свойство. Но это работает только в том случае, если ModifyStringMethod является статическим и не требует параметров вне класса.

public struct EllipsisString
{
    private string _value;

    public string Value {get { return _value; }}

    public EllipsisString(string value)
    {
        _value = value;
    }

    // implicit conversion from string so it is possible to just assign string to the property
    public static implicit operator EllipsisString(string value)
    {
        return new EllipsisString(value);
    }

    public static implicit operator string(EllipsisString original)
    {
        return SpecialMethod(original.Value);
    }

    public override string ToString()
    {
        return SpecialMethod(Value);
    }

    private static string SpecialMethod(string value)
    {
        return value + "...";
    }
}

И использование прост:

    private EllipsisString FirstName { get; set; }

    public void Method()
    {
        FirstName = "Thomas";
        Console.WriteLine(FirstName);
        Console.WriteLine(FirstName.Value);
    }

Ответ 3

PostSharp - еще одна альтернатива.

Вы просто применяете атрибут над классом и записываете свои свойства с помощью "get; set;" синтаксис.

PostSharp - это инструмент .NET, который позволяет разработчикам применять аспекты кода, который должен быть выполнен, там, где есть сборки, пространства имен, классы или методы.

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

Общие варианты использования включают следующее:

  • Вход

  • Безопасность

  • Отмена/Повтор

  • INotifyPropertyChanged

  • ExceptionHandling

Ответ 4

Когда я сталкивался с этим в прошлом, я использовал специальные фрагменты кода в VS, чтобы легко создавать свойства. См. Ссылку ЗДЕСЬ.

Затем, когда вам нужно добавить новое свойство, вы просто вызываете фрагмент кода и при необходимости заполняете имена элементов.

Хотя это не обязательно отрицает необходимость иметь много похожих свойств, это упрощает их создание (в том же порядке, что и использование шаблонов T4, как указано выше).

Ответ 5

Если вы можете сделать свои свойства виртуальными, вы можете использовать перехватчик с помощью динамического прокси-сервера Castle.

Перехватчик содержит поведение, которое может выполняться при вызове данного метода. В этом случае мы ModifyStringMethod возвращаем значение свойства string.

Как

1) Добавить ссылку на пакет nuget Castle.Core

2) Определите свой перехватчик

public class ModifyStringMethodInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        invocation.Proceed();
        if (invocation.Method.Name.StartsWith("get_") && 
            invocation.Method.ReturnType == typeof(string))
        {
            invocation.ReturnValue = 
              ModifyStringMethod((string)invocation.ReturnValue);
        }
    }

    private static string ModifyStringMethod(string input)
    {
        return (input ?? "") + "MODIFIED";
    }
}

В приведенном выше примере есть метод Intercept, который будет вызываться при вызове вашего свойства. Вы можете увидеть в примере invocation.Proceed(), это продолжает вызов свойства.

Затем он проверяет, является ли его свойство get_ и возвращает строку

 if (invocation.Method.Name.StartsWith("get_") &&
     invocation.Method.ReturnType == typeof(string))

Затем изменяет возвращаемое значение метода.

 invocation.ReturnValue = ModifyStringMethod((string)invocation.ReturnValue);

3) Определите свои объекты, к которым вы хотите добавить это поведение, с помощью виртуального метода (обратите внимание, что я также могу использовать автоматически реализованные свойства) BONUS

public class Intercepted
{
    public virtual string A { get; set; }
}

4) Затем создайте экземпляры объекта, используя класс ProxyGenerator в DynamicProxy,

например

 public class Program
{
    static void Main(string[] args)
    {
        var pg = new ProxyGenerator();

        //  Intercepted will be an instance of Intercepted class with the           
        //  ModifyStringMethodInterceptor applied to it 
        var intercepted = pg.CreateClassProxy<Intercepted>(new ModifyStringMethodInterceptor());

        intercepted.A = "Set ... ";
        Console.WriteLine(intercepted.A);
        Console.ReadLine();

    }
}

Выход

Set ... MODIFIED

Преимущество здесь в том, что ваши объекты "чисты", например, им не нужно знать о ModifyStringMethodInterceptor и может содержать автоматически реализованные свойства, которые, если у вас много таких объектов, уменьшат количество кода на большие суммы.

Переход на один шаг вперед, если вам нужно дальнейшее управление, вы можете применить это поведение, добавив в класс атрибут, например

[AttributeUsage(AttributeTargets.Method)]
public class ModifyStringMethodAttribute : Attribute
{
}

Затем объекты определяются как:

 public class Intercepted
 {
     public virtual string A { [ModifyStringMethod] get; set; }
 }

И переход к перехватчику:

if (invocation.Method.ReturnType == typeof(string) && 
    invocation.Method.GetCustomAttributes(true)
    .OfType<ModifyStringMethodAttribute>().Any())
        {
            invocation.ReturnValue = 
                  ModifyStringMethod((string)invocation.ReturnValue);
        }

Проверить атрибут и затем применить вызов метода.

Ответ 6

Однако я лично не поклонник этого решения, вы можете сделать что-то вроде этого:

class MyClass
{
    private IDictionary<string, string> propertyValueByName = new Dictionary<string, string>(); 

    public string this[string propertyName]
    {
        get { return propertyValueByName[propertyName]; }
        set { propertyValueByName[propertyName] = ModifyStringMethod(value); }
    }

    public string FirstName
    {
        get { return this["FirstName"]; }
        set { this["FirstName"] = value; }
    }

    public string LastName
    {
        get { return this["LastName"]; }
        set { this["LastName"] = value; }
    }
}

Ответ 7

Пример с использованием отражения

class MyClass
{
    public string FirstName { private get; set; }

    public string LastName { private get; set; }


    public string GetModifiedValue(string propertyName)
    {
        var prop = this.GetType().GetProperty(propertyName);
        return ModifyStringMethod((string)prop.GetValue(this, null));
    }
}

Итак, чтобы получить каждое измененное значение вместо использования MyClass.FirstName, вы должны использовать MyClass.GetModifiedValue("FirstName")

Ответ 8

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

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
            <Title>propnot</Title>
            <Shortcut>propnot</Shortcut>
            <Description>Code snippet for property and backing field with property change event</Description>
            <Author>Radin Gospodinov</Author>
            <SnippetTypes>
                <SnippetType>Expansion</SnippetType>
            </SnippetTypes>
        </Header>
        <Snippet>
            <Declarations>
                <Literal>
                    <ID>type</ID>
                    <ToolTip>Property type</ToolTip>
                    <Default>int</Default>
                </Literal>
                <Literal>
                    <ID>property</ID>
                    <ToolTip>Property name</ToolTip>
                    <Default>MyProperty</Default>
                </Literal>
                <Literal>
                    <ID>field</ID>
                    <ToolTip>The variable backing this property</ToolTip>
                    <Default>myVar</Default>
                </Literal>
            </Declarations>
            <Code Language="csharp">
      <![CDATA[private $type$ $field$;

    public $type$ $property$
    {
        get { return this.$field$;}
        set {
    if(this.$field$ != value)   {
        $field$ = value;
        this.RaisePropertyChanged(() => this.$property$);
      }
      }
    }
    $end$]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>

Ответ 9

Если вы действительно хотите пойти с этим подходом, то генерация кода, использующая что-то вроде шаблонов T4, или CodeSmith, вероятно, способ пойти, однако я согласен с @DanBryant, что создание ваших свойств, подобных этому, может привести к тому, что счетчик будет интуитивно понятным класс. Я бы ожидал, что такой код будет работать:

X.FirstName = "Some random long name";
Assert.AreEqual("Some random long name", X.FirstName);

Из вашего комментария, с вашим дизайном класса это может не сработать (в зависимости от длины усечения в вашем ModifyStringMethod вы действительно можете получить X.FireName == "Some rand...". Это кажется неправильным.

Лучшим подходом может быть реализация модификации вне поведения свойства, возможно, в методе расширения. Так что-то вроде этого:

public static class FormatStringExtensions {
    public static string ModifyStringForOutput(this string me) {
        if (me.Length > 10) {
            return me.Substring(0, 10) + "...";
        }
        return me;
    }
};

Позволит вам определить ваши классы данных с использованием автоматических свойств:

public class myclass {
    public string FirstName { get; set; }
    public string LastName {get; set; }
};

И затем измените значение строки из свойств как и когда оно будет использоваться с использованием метода расширения:

    var instance = new myclass();

    instance.FirstName = "01234567890123";

    Console.WriteLine("Original Name {0}\nModified Name {1}\n", 
                      instance.FirstName,
                      instance.FirstName.ModifyStringForOutput());

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

Ответ 10

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

Ответ 11

Вы можете определить свой собственный класс для наследования из DynamicObject

public class MyExpando : DynamicObject
{
    Dictionary<string, object> dictionary = new Dictionary<string, object>();

    //Want to create properties on initialization? Do it in the constructor
    public MyExpando()
    {
        dictionary.Add("PreferredName", "Darth Sidious");
        dictionary.Add("GreatDialog", "Something, something, darkside!");
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        bool success = dictionary.TryGetValue(binder.Name, out result);
        if (success)
            result = ModifiedValue(result); 
        return success;
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        dictionary[binder.Name] = value;
        return true;
    }
    private string ModifiedValue(object val)
    {
        //Modify your string here.

        if (val.ToString() != "Darth Sidious")
            return "Something something complete";
        return val.ToString();
    }
}

Хотите создать свойство не в конструкторе? Тогда вы можете просто сделать

dynamic x = new MyExpando();
x.FirstName = "Sheev";
x.LastName = "Palpatine"
Console.WriteLine(x.PreferredName + " says : \"" + x.GreatDialog + "\"");
Console.ReadKey();

Великая вещь в том, что вы можете реализовать INotifyPropertyChanged, а затем вызывать его в вашем методе TrySetMember, вы также можете ограничить доступ к вашему "Настройщику" свойств или getter, просто выбросив исключение, основанное на имени свойства в TryGetMember или TrySetMember, и просто измените модификатор доступа словаря для классов, которые наследуют от MyExpando, чтобы имитировать наследование свойств.

Пример того, как ограничить доступ к устройству свойств.

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        if (!dictionary.ContainsKey(binder.Name))
            return false;
        dictionary[binder.Name] = value;
        return true;
    }