To be honest I wasn't sure how to word this question so forgive me if the actual question isn't what you were expecting based on the title. C# is the first statically typed language I've ever programmed in and that aspect of it has been an absolute headache for me so far. I'm fairly sure I just don't have a good handle on the core ideas surrounding how to design a system in a statically typed manner.
Here's a rough idea of what I'm trying to do. Suppose I have a hierarchy of classes like so:
abstract class DataMold<T>
{
public abstract T Result { get; }
}
class TextMold : DataMold<string>
{
public string Result => "ABC";
}
class NumberMold : DataMold<int>
{
public int Result => 123
}
Now suppose I want to make a list of item where the items can be any kind of mold and I can get the Result
property of each item in a foreach
loop like so:
List<DataMold<T>> molds = new List<DataMold<T>>();
molds.Add(new TextMold());
molds.Add(new NumberMold());
foreach (DataMold<T> mold in molds)
Console.WriteLine(mold.Result);
As you probably already know, that doesn't work. From what I've read in my searches, it has to do with the fact that I can't declare the List to be of type DataMold<T>
. What is the correct way to go about something like this?
In Java, a method can be invoked from another class based on its access modifier. For example, a method created with a public modifier can be called from inside as well as outside of a class/package. The protected method can be invoked from another class using inheritance.
Class B inherits from Class A, and I want class A to be able to call a function created in class B. using namespace std; class B; class A { public: void CallFunction () { B b; b. bFunction(); } }; class B: public A { public: virtual void bFunction() { //stuff done here } };
A function has another property: all calls to a function with the same parameters, should return the same result. A method, on the other hand, is a function that is related to an object in an object-oriented language. It has one implicit parameter: the object being acted upon (and it's state).
Definition. A class is a template for creating or instantiating objects within a program while a method is a function that exposes the behavior of an object. Thus, this is the main difference between class and method.
The short answer: You can't.
One of the things that is counterintuitive about generic types is that they are not related. A List<int>
, for example, has no relationship whatsoever to a List<string>
. They do not inherit from each other, and you can't cast one to the other.
You can declare a covariance relationship, which looks a lot like an inheritance relationship, but not between an int
and a string
as you have declared, since one is a value type and one is a reference type.
Your only alternative is to add another interface that they have in common, like this:
interface IDataMold
{
}
abstract class DataMold<T> : IDataMold
{
public abstract T Result { get; }
}
Now you can store all of your molds in a List<IDataMold>
. However, the interface has no properties, so you'd have a heckuva time getting anything out of it. You could add some properties, but they would not be type-specific, as IDataMold
has no generic type parameter. But you could add a common property
interface IDataMold
{
string ResultString { get; }
}
...and implement it:
abstract class DataMold<T>
{
public abstract T Result { get; }
public string ResultString => Result.ToString();
}
But if your only need is to display a string equivalent for each item, you can just override ToString() instead:
class TextMold : DataMold<string>
{
public string Result => "ABC";
public override string ToString() => Result.ToString();
}
Now you can do this:
List<IDataMold> molds = new List<IDataMold>();
molds.Add(new TextMold());
molds.Add(new NumberMold());
foreach (var mold in molds)
{
Console.WriteLine(mold.ToString());
}
You're looking for covariance. See the out
keyword before T
generic type parameter:
// Covariance and contravariance are only possible for
// interface and delegate generic params
public interface IDataMold<out T>
{
T Result { get; }
}
abstract class DataMold<T> : IDataMold<T>
{
public abstract T Result { get; }
}
class StringMold : DataMold<string> {}
class Whatever {}
class WhateverMold : DataMold<Whatever> {}
Now inherit DataMold<T>
and create a List<IDataMold<object>>
:
var molds = new List<IDataMold<object>>();
molds.Add(new StringMold());
molds.Add(new WhateverMold());
BTW, you can't use covariance when it comes to cast IDataMold<int>
to IDataMold<object>
. Instead of repeating what's been already explained, please see this other Q&A: Why covariance and contravariance do not support value type
If you're really forced to implement IDataMold<int>
, that list may be of type object
:
var molds = new List<object>();
molds.add(new TextMold());
molds.add(new NumberMold());
And you may use Enumerable.OfType<T>
to get subsets of molds
:
var numberMolds = molds.OfType<IDataMold<int>>();
var textMolds = molds.OfType<IDataMold<string>>();
Also, you may create two lists:
var numberMolds = new List<IDataMold<int>>();
var textMolds = new List<IDataMold<string>>();
So you might mix them later as an IEnumerable<object>
if you need to:
var allMolds = numberMolds.Cast<object>().Union(textMolds.Cast<object>());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With