jeudi 22 mai 2014

Création d'une méthode générique virtual en c# - Stack Overflow


I have some base classes like this:


public class AbstractData
{
public int ID { get; set; }
}

public class Person: AbstractData
{
public string Name { get; set; }
}

public class AbstractManager<T> where T: AbstractData
{
public virtual List<T> GetAll()
{
}

public virtual T GetOne(int id)
{
}
}

public class PersonManager: AbstractManager<Person>
{
public override List<Person> GetAll()
{
//...
}

public override Person GetOne(int id)
{
//...
}
}

Now, I have a Windows Forms base class, like this:


public class BaseForm: Form
{
public virtual AbstractManager<T> GetManager<T>() where T: AbstractData
{
return null;
}
}

and a derived form:


public class PersonForm: BaseForm
{
public override AbstractManager<T> GetManager<T>()
{
return new PersonManager();
}
}

The problem is, I keep getting compile errors on the PersonForm class:



Cannot implicitly convert type 'PersonManager' to 'AbstractManager<T>'



Is there a way in which I can create this virtual method and have every class derived from BaseForm return the concrete representation of the AbstractManager?


If I get rid of the generic on the AbstractManager class then I compile OK (with a few code changes), but then the GetAll method can't return a List<T>. It would have to return a List<AbstractData> instead, which causes issues in converting from List<Person> to List<AbstractData>.


Any help would be appreciated.




First off all, please never do this:


class C<T>
{
void M<T>(T t) { }
}

Now we have two things named T both in scope and they are different. This is legal but extremely confusing. Choose better names for your type parameters.


Let's simplify your example:


class FruitBasket<T> where T : Fruit { }
class AppleBasket : FruitBasket<Apple> { }
class C
{
public static FruitBasket<T> GetBasket<T>() where T: Fruit
{
return new AppleBasket();
}
}

Now do you see why this is wrong? What if someone calls C.GetBasket<Orange>() and you hand them a basket of apples?



Any help would be appreciated.



What's step one of getting out of a hole? STOP DIGGING.


You have Genericity Happiness Disease, which is common to C# programmers who are discovering the power of the generic type system and then want to use it for everything whether that makes sense or not. Stop trying to capture all the relationships in your business process in the generic type system; that's not what it was designed for.


The test is: can you say "an apple basket is a basket of apples, where apples are a kind of fruit" and have someone who is not a programmer agree with you? Yes. Can you say "a person manager is an abstract manager of persons where person is a kind of abstract data" and have someone who is not a programmer agree with you? No. Then you are not successfully modeling the business domain in the type system. Start over, avoid generics, and try to come up with relationships between types that make sense.




By declaring


public virtual AbstractManager<T> GetManager<T>() where T: AbstractData

in BaseForm, you're promising that every class derived from BaseForm supports GetManager for any type T. For example, if you had another AbstractData subclass named Invoice, then you could write


personForm.GetManager<Invoice>()

and PersonForm would be expected to return an InvoiceManager.


If you want every class derived from BaseForm to support GetManager for only one type T, then move the T type parameter from GetManager to BaseForm:


public class BaseForm<T>: Form where T: AbstractData
{
public virtual AbstractManager<T> GetManager()
{
return null;
}
}

public class PersonForm: BaseForm<Person>
{
public override AbstractManager<Person> GetManager()
{
return new PersonManager();
}
}

UPDATE: Chad Henderson points out that the Windows Forms designer can't handle generic base classes. If that's a problem for you, then you could try an alternate approach:


public interface IForm<T> where T: AbstractData
{
AbstractManager<T> GetManager();
}

public class BaseForm: Form
{
// ... base functionality that doesn't depend on T ...
}

public class PersonForm: BaseForm, IForm<Person>
{
public AbstractManager<Person> GetManager()
{
return new PersonManager();
}
}


I have some base classes like this:


public class AbstractData
{
public int ID { get; set; }
}

public class Person: AbstractData
{
public string Name { get; set; }
}

public class AbstractManager<T> where T: AbstractData
{
public virtual List<T> GetAll()
{
}

public virtual T GetOne(int id)
{
}
}

public class PersonManager: AbstractManager<Person>
{
public override List<Person> GetAll()
{
//...
}

public override Person GetOne(int id)
{
//...
}
}

Now, I have a Windows Forms base class, like this:


public class BaseForm: Form
{
public virtual AbstractManager<T> GetManager<T>() where T: AbstractData
{
return null;
}
}

and a derived form:


public class PersonForm: BaseForm
{
public override AbstractManager<T> GetManager<T>()
{
return new PersonManager();
}
}

The problem is, I keep getting compile errors on the PersonForm class:



Cannot implicitly convert type 'PersonManager' to 'AbstractManager<T>'



Is there a way in which I can create this virtual method and have every class derived from BaseForm return the concrete representation of the AbstractManager?


If I get rid of the generic on the AbstractManager class then I compile OK (with a few code changes), but then the GetAll method can't return a List<T>. It would have to return a List<AbstractData> instead, which causes issues in converting from List<Person> to List<AbstractData>.


Any help would be appreciated.



First off all, please never do this:


class C<T>
{
void M<T>(T t) { }
}

Now we have two things named T both in scope and they are different. This is legal but extremely confusing. Choose better names for your type parameters.


Let's simplify your example:


class FruitBasket<T> where T : Fruit { }
class AppleBasket : FruitBasket<Apple> { }
class C
{
public static FruitBasket<T> GetBasket<T>() where T: Fruit
{
return new AppleBasket();
}
}

Now do you see why this is wrong? What if someone calls C.GetBasket<Orange>() and you hand them a basket of apples?



Any help would be appreciated.



What's step one of getting out of a hole? STOP DIGGING.


You have Genericity Happiness Disease, which is common to C# programmers who are discovering the power of the generic type system and then want to use it for everything whether that makes sense or not. Stop trying to capture all the relationships in your business process in the generic type system; that's not what it was designed for.


The test is: can you say "an apple basket is a basket of apples, where apples are a kind of fruit" and have someone who is not a programmer agree with you? Yes. Can you say "a person manager is an abstract manager of persons where person is a kind of abstract data" and have someone who is not a programmer agree with you? No. Then you are not successfully modeling the business domain in the type system. Start over, avoid generics, and try to come up with relationships between types that make sense.



By declaring


public virtual AbstractManager<T> GetManager<T>() where T: AbstractData

in BaseForm, you're promising that every class derived from BaseForm supports GetManager for any type T. For example, if you had another AbstractData subclass named Invoice, then you could write


personForm.GetManager<Invoice>()

and PersonForm would be expected to return an InvoiceManager.


If you want every class derived from BaseForm to support GetManager for only one type T, then move the T type parameter from GetManager to BaseForm:


public class BaseForm<T>: Form where T: AbstractData
{
public virtual AbstractManager<T> GetManager()
{
return null;
}
}

public class PersonForm: BaseForm<Person>
{
public override AbstractManager<Person> GetManager()
{
return new PersonManager();
}
}

UPDATE: Chad Henderson points out that the Windows Forms designer can't handle generic base classes. If that's a problem for you, then you could try an alternate approach:


public interface IForm<T> where T: AbstractData
{
AbstractManager<T> GetManager();
}

public class BaseForm: Form
{
// ... base functionality that doesn't depend on T ...
}

public class PersonForm: BaseForm, IForm<Person>
{
public AbstractManager<Person> GetManager()
{
return new PersonManager();
}
}

0 commentaires:

Enregistrer un commentaire