Assign a sub class to an interface, with generics and covariant involved - c#

I defined 3 interfaces:
public interface IManufacturerInput
{
}
public interface IManufacturerOutput
{
}
public interface IManufacturerApi<in S, out T>
where S : IManufacturerInput
where T : IManufacturerOutput
{
T Calculate(S);
}
And I defined a specific Manufacturer:
public class ManufacturerAInput : IManufacturerInput
{
}
public class ManufacturerAOutput : IManufacturerOutput
{
}
public class ManufacturerAApi : IManufacturerApi<ManufacturerAInput, ManufacturerAOutput>
{
public ManufacturerAOutput Calculate(ManufacturerAInput)
{
return null;
}
}
And In Main() I created a ManufacturerAApi, and try assign it to IManufacturerApi.
IManufacturerApi<IManufacturerInput, IManufacturerOutput> api = new ManufacturerAApi();
But it failed. The error message said (just abstract meaning):
Can't convert from ManufacturerAApi to IManufacturerApi<IManufacturerInput, IManufacturerOutput>
So is there any way I can make the assignment work? Thanks in advance.

What you are proposing isn't type safe. Let's change the names of your types to make the issue clearer:
public interface IPetFood { }
public interface IPetSound { }
public interface IPetCage<in S, out T>
where S : IPetFood
where T : IPetSound
{
T Feed(S s);
}
public class DogFood : IPetFood { }
public class CatFood : IPetFood { }
public class Bark : IPetSound { }
public class DogCage : IPetCage<DogFood, Bark>
{
public Bark Feed(DogFood input)
{
return new Bark();
}
}
And now suppose this is legal:
IPetCage<IPetFood, IPetSound> api = new DogCage();
Then we could do the following:
api.Feed(new CatFood()); //oops we've just given the dog some catfood.
The assignment will not work because S is contravariant, which means that any possible IPetFood passed into api.Feed would need to be a subtype of DogFood and you have the opposite; IPetFood is a superset of DogFood.

Related

Use abstract generic type in c# and requires 1 type arguments

Let's say I have follow classes:
public class File { }
public class DB { }
public abstract class Validator<T>
{
T obj;
}
public class FileValidator : Validator<File>
{
}
public class DbValidator : Validator<DB>
{
}
In code I would like to have a ref to abstract class and create certain implementation depends on some condition (like factory).
public class Program
{
static Validator getValidator()
{
//some condition here
return new FileValidator<File>();
}
public static void Main()
{
Validator v = getValidator();
}
}
So, the compiler has a different opinion on this point
Using the generic type 'Validator' requires 1 type arguments
Is there any workaround in this case? Unfortunately, File and DB can't have common interface.
You need a non-generic interface or abstract base-class:
public interface IValidator { ... }
public abstract class Validator<T> : IValidator
{
}
Now you can return IValidator from your method:
static IValidator getValidator()
{
//some condition here
return new FileValidator<File>();
}
public static void Main()
{
IValidator v = getValidator();
}
However be aware that there is no way for the compiler to infer the actual type, which is based upon a runtime-decision.
Apart from this your implementing classes should not be generic at all, only your abstract class should be:
public class FileValidator : Validator<File>
{
}
public class DbValidator : Validator<DB>
{
}

How to assign a concrete implementation of generic type to list of generic type

I have been trying to understand why the following generates a compile error, and how to get around the error.
public interface IA { }
public class AImp : IA { }
public interface IConsumer<T> where T : IA
{
void Consume(T val);
}
public class Consumer : IConsumer<AImp>
{
public void Consume(AImp val)
{
// do smth
}
}
public class Program
{
public static void Main()
{
IList<IConsumer<IA>> l1 = new List<IConsumer<IA>>();
l1.Add(new Consumer()); // generate compile error cannot convert from consumer to IConsumer<IA>
}
}
If I can create IList<IA> l = new List<IA>(); and asign l.Add(new AImp()); , I dont see why the generic type is not working. Perhaps I am missing something fundamental.
I haven't been able to find any promising leads from google either.
You get an error message because the Consumer does not inherit from IConsumer<T>. It inherits from IConsumer<AImp>.
There is special technique to use this kind of template class, you have to create a IConsumer interface which is not generic type like this:
public interface IA { }
public class AImp : IA { }
public interface IConsumer
{
}
public interface IConsumer<T> : IConsumer
where T : IA
{
void Consume(T val);
}
public class Consumer : IConsumer<AImp>
{
public void Consume(AImp val)
{
// do smth
}
}
public class Program
{
public static void Main()
{
IList<IConsumer> l1 = new List<IConsumer>();
l1.Add(new Consumer()); // ok
}
}
This basically the idea behind the IEnumerable and ICollection generic and non generic versions.

How To cast a child class to base class when base class is generic in c#

I have these classes:
public class BaseGame<T> where T : BaseQuestion
{
//some fileds and methods
}
public class BaseQuestion
{
//some fileds and methods
}
public class Question : BaseQuestion
{
//some fileds and methods
}
public class SampleGame : BaseGame<Question>
{
//some fileds and methods
}
Whenever i want to cast BaseGame to SampleGame i get Error.
void SetValue(BaseGame<BaseQuestion> _game)
{
SampleGame = (SampleGame) _game;
}
Cannot implicitly convert type BaseGame to SampleGame.
How To Fix this?
Thanks.
Change your code to this:
public interface IBaseGame<out T> where T : BaseQuestion
{
}
public class BaseGame<T> : IBaseGame<T>
where T : BaseQuestion
{
}
public class BaseQuestion
{
}
public class Question : BaseQuestion
{
}
public class SampleGame : BaseGame<Question>
{
}
And then your method:
void SetValue(IBaseGame<BaseQuestion> _game)
{
var SampleGame = (SampleGame) _game;
}
Why was your code broken?
You cannot do the cast because BaseGame was not covariant. This means that the compile cannot gaurantee that the cast is safe. Take for example, the following example:
public class BaseGame<T> where T : BaseQuestion
{
private List<T> Questions { get; set; }
public void AddQuestion(T question) { Questions.Add(question); }
}
var game = new BaseGame<Question> { Questions = new List<Question> };
game.AddQuestion(new Question());
Okay, so now your game has a list of Question, with one question in it. Now let's cast it:
public class BrokenQuestion : BaseQuestion
{
}
public class BrokenGame : BaseGame<BrokenQuestion>
{
}
var brokenGame = (BrokenGame)game;
brokenGame.Add(new BrokenQuestion());`
Oops! We just added a BrokenQuestion to a List<Question>. No good.
Defining the template argument as out T instead of T means we're making a contract that we will not accept sub classes of T, but we can return them. This means that it's now illegal to expose AddQuestion(T question). Now, the compiler can be sure that there will never be a BrokenQuestion added to List<Question>.
For more information, see here

Protected generic class - is it supported?

I had a question on C# generics. I wish to store a generic type variable in my abstract class without declaring that type outside the class.
Below is the code sample. Please note that I do not wish to make the Param classes exposed outside the Calc class.
Thanks in advance.
- Dutta.
abstract class Base { }
abstract class Calc<T> where T : Base
{
protected Param Member; /* how can this be a made a generic declaration
* WITHOUT declaring this class like,
* class Calc<T, P>
* where T : Base
* where P : Param */
protected Calc(Param p)
{
this.Member = p;
}
protected abstract class Param { }
}
class MyBase : Base { }
class MyCalc : Calc<MyBase>
{
public MyCalc() : base(new MyParam()) { }
public void doSomething()
{
base.Member.A++; // fails on compilation
}
private class MyParam : Calc<MyBase>.Param
{
public int A;
public MyParam() { this.A = 0; }
}
}
You just need to cast it to the new type, because no matter what, the variable Member was declared as Param and it will always be accessed as Param:
((MyParam)base.Member).A++;
Secondly, you can fix up your MyParam class by changing from this:
MyParam : Calc<MyBase>.Param
To this:
MyParam : Param
Because Param is already Calc<MyBase> through generics and inheritance.
Thraka's answer is correct: if you don't want to use generics you need to cast. Just to add to it, in case what you're really trying to do looks something like this. Here's a set of classes that you can expose from your library, which will not be extensible by clients (unless they're running with full trust and can use reflection etc.!!) but which can be used in a type-safe way.
public abstract class SupportedPaymentMethod
{
protected internal SupportedPaymentMethod() { }
}
public sealed class Check : SupportedPaymentMethod
{
public int CheckNumber { get; private set; }
public Check(int checkNumber)
: base()
{
CheckNumber = checkNumber;
}
}
public sealed class CreditCard : SupportedPaymentMethod
{
public CreditCard()
: base()
{ }
}
public abstract class Payment<T>
where T : SupportedPaymentMethod
{
public T Method { get; private set; }
protected internal Payment(T method)
{
Method = method;
}
}
public sealed CheckPayment : Payment<Check>
{
public CheckPayment(Check check)
: base(check)
{ }
}
public sealed CreditCardPayment : Payment<CreditCard>
{
public CreditCardPayment(CreditCard creditCard)
: base(creditCard)
{ }
}
Clients (i.e. code outside of your class library's assembly) will be able to instantiate a CheckPayment or a CreditCardPayment, but they will not be able to create a new class deriving from Payment<T>. So, it will not be possible for clients to create a CheatingPaymentMethod : Payment<Cheating>, for example. :)
Calls like your intended call to base.Member.A++ will now work:
var checkPayment = new CheckPayment(new Check(123456));
var checkNumber = checkPayment.Method.CheckNumber; // Success! :)

Inheriting from a class that inherits from a abstract class

I trying to inherit a class Blah2, but after adding a method it says BlahA doesn't implement that method.
How can I add a method to my new class?
public class Blah2 : BlahA
{
}
public class Blah3 : Blah2
{
public List<int> MyNewMethod()
{
}
}
Note: BlahA is an abstract class.
Update
public abstract class BlahA : IBlah
{
}
Update II - the error
Error 3 'Blah.Components.BlahA' does not contain a definition for 'Blah3' and no extension method 'Blah3' accepting a first argument of type 'Blah.Components.BlahA' could be found (are you missing a using directive or an assembly reference?)
Well if it's implementing an interface as you posted in your comments, then the problem is that your BlahA class doesn't satisfy the requirements of the interface. There must be some method in the interface (I'm assuming its the MyNewMethod) that you're not implementing in your abstract BlahA class.
If my assumption is correct, add this to your base class:
public abstract List<int> MyNewMethod();
and in your sub class, add the word override to your method declaration.
Some code:
public interface MyInterface
{
void MyMethod();
}
public abstract class Base : MyInterface
{
public abstract void MyMethod();
}
public class SubA : Base
{
public override void MyMethod()
{
throw new NotImplementedException();
}
}
public class SubB : SubA
{
public void Foo() { }
}
Wrting this code and compiling works fine
public abstract class BlahA
{
}
public class Blah2 : BlahA
{
}
public class Blah3 : Blah2
{
public List<int> MyList()
{
return new List<int>();
}
}
We will need a bit more of the code that isnt working
EDIT:
from comments you need to implement the method from interface in abstract class.
public interface IBlah
{
int GetVal();
}
public abstract class BlahA : IBlah
{
public int GetVal()
{
return 1;
}
}
public class Blah2 : BlahA
{
}
public class Blah3 : Blah2
{
public List<int> MyList()
{
int i = GetVal();
return new List<int>();
}
}

Categories

Resources