Универсальный метод и это

#c# #generics

#c# #универсальные

Вопрос:

Я хочу создать универсальный метод сохранения, который должен работать следующим образом;

 Entity.User.User user = new Entity.User.User();
user.FirstName = "A";
user.LastName = "B";
user.UserName = "C";
user.Save();

public class User : BaseClass<User>
{
    public virtual int UserId { get; set; }
    public virtual string UserName { get; set; }
    public virtual string Password { get; set; }
    public virtual string FirstName { get; set; }
    public virtual string LastName { get; set; }

    //I WANT THIS METHOD TO BE GENERIC UNDER BASE CLASS
    //public void Save() 
    //{
    //    CREATE.New(this);
    //}
}

public partial class BaseClass<T>
{
    public void Save(this T x)
    {
        CREATE<T>.New(x);
    }
}

public class CREATE<T>
{
    public static void New(T x)
    {
        //some code
    }
}
  

но я получаю сообщение об ошибке «Метод расширения должен быть определен в неродовом статическом классе»

ищу руководство по управлению этой работой. Спасибо

Комментарии:

1. методы расширения также не могут быть виртуальными; что вы пытаетесь сделать? если это базовый класс, то он this просто обычный this — в this T x ; просто CREATE<T>.New(this) . CREATE<T> кстати, ужасное имя.

2. Извините, но разве это не циклическая ссылка? public class User : BaseClass<User>

3. Я исправил виртуальный. обновлено

4. Класс CREATE должен быть статическим, если в нем есть статический метод.

5. Ключевое слово @Marc ‘this’ недопустимо в статическом свойстве, статическом методе или инициализаторе статического поля

Ответ №1:

Чтобы совместно использовать метод сохранения со всеми вашими «сущностями», вам не нужен ни общий родительский класс, ни методы расширения.

это всего лишь простое наследство:

 public class User : BaseClass
{
    public int UserId { get; set; }
    public string UserName { get; set; }
    public string Password { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class BaseClass
{
    public void Save()
    {
       //put save logic here. Should match for all 
       //concrete implementations of BaseClass
    }
}
  

теперь вы просто можете вызвать

 User user = new User();
user.FirstName = "A";
user.LastName = "B";
user.UserName = "C";
user.Save();
  

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

Конкретная реализация зависит от того, как вы сохраняете. Если это просто сериализация, вы могли бы придерживаться одного глобального метода сохранения в базовом классе, и дочерним элементам не нужно предоставлять какую-либо реализацию в save().


т. е. для построения строки вы могли бы сделать что-то вроде этого: Таким образом, КАЖДЫЙ класс просто предоставляет ту часть, за которую он отвечает:

 public class User : BaseClass
{
    public string UserName { get; set; }
    public string Password { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public override String Save(){
        s = base.Save(); //recall base.Save() as long as its not the topmost-entity
        //attach Username, password, firstname, lastname
        return s;
    }
}

public class BaseClass
{
    public int Id { get; set; }

    public virtual String Save()
    {
       String s = "";
       //attach id to s
       return s; 
     }
}
  

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

На практике вы бы создали не строку, а, возможно, XML-элемент или подготовленную инструкцию, готовую к запуску с базой данных.

Комментарии:

1. извините, если я что-то пропустил, но где пользователь в качестве входных данных? и как мне создать логику, над которой я не знаю, над каким типом класса я работаю. не могли бы вы заполнить внутри сохранения, чтобы я лучше понял.

2. @Mert вам не нужен пользователь в качестве входных данных. он унаследует метод, как и любой другой метод. Вы всегда можете узнать, с каким конкретным классом вы работаете ( if (this is User) ), И конкретная реализация сохранения зависит от того, как вы сохраняете.

3. Это сработало, но я потерял всю общую функциональность, я выясню, наносит ли это какой-либо вред моему общему решению. Спасибо

4. Я хотел бы услышать от вас комментарий о решении Марка. Кажется, вы, ребята, более опытны в этом вопросе.

Ответ №2:

С вашим общим подходом, я думаю, что вам нужно, это:

 public partial class BaseClass<T>
{
    public virtual void Save()
    {
        // only support the T : BaseClass<T> scenario, so
        // we expect "this" to be a T
        CREATE<T>.New((T)(object)this);
    }
}
  

Комментарии:

1. Я попробовал ваше решение, и оно сработало. кроме того, я не потерял общую функциональность, которая выглядит как лучшее решение. спасибо, я новичок в дженериках, поэтому мне требуется время, чтобы полностью разобраться. Спасибо

Ответ №3:

Вам нужно изменить класс CREATE на:

 public static class CREATE
{
    public static void New<T>(this T x)
    {
        //some code
    }
}
  

Это ваш метод расширения, они должны быть в статическом неродовом классе. Как и ошибка, на которую вам указали.

И вам нужно изменить Save метод на:

 public void Save(T x)
{
    x.Save();
}
  

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

Комментарии:

1. И он должен изменить метод сохранения на: public void Save() { CREATE.New<T>(this); }

2. добавление этого при новой созданной ошибке this ; Метод расширения должен быть определен в неродовом статическом классе И удаление <T> из CREATE<T> вызывает эту ошибку; Не удалось найти тип или имя пространства имен ‘T’ (вам не хватает директивы using или ссылки на сборку?)