user1692315
user1692315

Reputation: 149

Get the exact type as property in C# class

Sorry for the general title but it's a bit hard to explain in few words what is my problem currently.

So I have a simple class factory like this:

    public Model Construct<T>(T param) where T : IModelable
    {
        new Model {Resource = param};
        return n;
    }

The Model class looks like this:

public class Model
{
    public object Resource { get; set; }
}

The problem is, that you can see, is the Resource is currently an object. And I would like that Resource should be the type, what is get from the Construct and not lost the type-safe...

I tried to solve it with type parameter but it fails, because I can extend Model class with type parameter but what if I would like to store it to a simple class repository?

Then Construct will work, but if I would like to get the instanced class from the repository, I have to declare the type paramter again like:

Repository.Get<Model<Spaceship>>(0) .... and of course it's wrong because I would like that Model itself knows, what type of Resource has been added in Construct.

Does anybody any idea how to handle this?

The whole code currently look like this:

/// <summary>
///     Class Repository
/// </summary>
public sealed class Repository
{
    /// <summary>
    ///     The _lock
    /// </summary>
    private static readonly object _lock = new object();

    /// <summary>
    ///     The _syncroot
    /// </summary>
    private static readonly object _syncroot = new object();

    /// <summary>
    ///     The _instance
    /// </summary>
    private static volatile Repository _instance;

    /// <summary>
    ///     The _dict
    /// </summary>
    private static readonly Dictionary<int, object> _dict
        = new Dictionary<int, object>();


    /// <summary>
    ///     Prevents a default data of the <see cref="Repository" /> class from being created.
    /// </summary>
    private Repository()
    {
    }

    /// <summary>
    ///     Gets the items.
    /// </summary>
    /// <value>The items.</value>
    public static Repository Data
    {
        get
        {
            if (_instance == null)
            {
                lock (_lock)
                {
                    if (_instance == null) _instance = new Repository();
                }
            }
            return _instance;
        }
    }

    /// <summary>
    ///     Allocates the specified id.
    /// </summary>
    /// <param name="id">The id.</param>
    /// <param name="parameter">The parameter.</param>
    /// <resource name="id">The id.</resource>
    /// <resource name="parameter">The parameter.</resource>
    public void Allocate(int id, object parameter)
    {
        lock (_syncroot)
        {
            _dict.Add(id, parameter);
        }
    }

    /// <summary>
    ///     Gets the specified id.
    /// </summary>
    /// <typeparam name="T">The type of the tref.</typeparam>
    /// <param name="id">The id.</param>
    /// <returns>``0.</returns>
    /// <resource name="id">The id.</resource>
    public T Get<T>(int id)
    {
        lock (_syncroot)
        {
            return (T) _dict[id];
        }
    }
}

/// <summary>
///     Class IModelFactory
/// </summary>
public sealed class ModelFactory
{
    /// <summary>
    ///     The _lock
    /// </summary>
    private static readonly object _lock = new object();

    /// <summary>
    ///     The _instance
    /// </summary>
    private static volatile ModelFactory _instance;

    /// <summary>
    ///     Prevents a default instance of the <see cref="ModelFactory" /> class from being created.
    /// </summary>
    private ModelFactory()
    {
    }

    /// <summary>
    ///     Gets the data.
    /// </summary>
    /// <value>The data.</value>
    public static ModelFactory Data
    {
        get
        {
            if (_instance == null)
            {
                lock (_lock)
                {
                    if (_instance == null) _instance = new ModelFactory();
                }
            }
            return _instance;
        }
    }

    /// <summary>
    ///     Constructs the specified param.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="param">The param.</param>
    /// <returns>Model{``0}.</returns>
    public Model Construct<T>(T param) where T : IModelable
    {
        var n = new Model {Resource = param};
        return n;
    }
}

/// <summary>
///     Class Model
/// </summary>
/// <typeparam name="T"></typeparam>
public class Model
{
    public object Resource { get; set; }
}

/// <summary>
///     Interface IModelable
/// </summary>
public interface IModelable
{
    /// <summary>
    ///     Gets or sets the mass.
    /// </summary>
    /// <value>The mass.</value>
    float Mass { get; set; }
}

/// <summary>
///     Class spaceship
/// </summary>
public class Spaceship : IModelable
{
    /// <summary>
    ///     Gets or sets the mass.
    /// </summary>
    /// <value>The mass.</value>
    public float Mass { get; set; }
}

So the problem will be lighted here:

Add to the Repository:

Repository.Data.Allocate(1, ModelFactory.Data.Construct(new Spaceship()));

It's okay, but after:

var test_variable = Repository.Data.Get<Model>(1);

So now I have a non type-safe object from a type parameter, I don't know, that what type of class has been stored with the c model construction.

I'm very thankful for the suggestions of using type paramter on the Model class as well, but than it will come up another problem, because I have to change the Get function with it:

var test_variable = Repository.Data.Get<Model<Spaceship>>(1);

But that's definitely wrong, because I won't know, that what kind of type of class has been stored in the model..., so I would like to achieve to avoid this type parameter definition when I would like to load the instance from the Repository.

Upvotes: 2

Views: 190

Answers (3)

Steven Doggart
Steven Doggart

Reputation: 43743

You can solve this by making your Model class generic, like this:

public class Model<T> 
{
    public T Resource { get; set; }
}

Then, your Construct method could work like this:

public Model<T> Construct<T>(T param) where T : IModelable<T>
{
    return new Model<T>() {Resource = param};
}

Upvotes: 4

Tim S.
Tim S.

Reputation: 56566

This sort of structure is one approach you could take:

public Model<T> Construct<T>(T param) where T : IModelable
{
    var n = new Model<T> {Resource = param};
    return n;
}
public class Model<T> : IModel<T> where T : IModelable
{
    public T Resource { get; set; }
}
public interface IModel<out T> where T : IModelable
{
    T Resource { get; }
}

This covariant interface allows you to refer to the types more generically where you wish, in the same way that you can pass an IEnumerable<string> into something expecting an IEnumerable<object>.

IModel<Spaceship> shipModel = // whatever
IModel<IModelable> model = shipModel;
//or even:
List<Model<Spaceship>> shipModels = // whatever
IEnumerable<IModel<IModelable>> models = shipModels;

Upvotes: 0

thepirat000
thepirat000

Reputation: 13124

You probably need a generic type in the model class:

public class Model<T>
{
    public T Resource { get; set; }
}

Upvotes: 1

Related Questions