隐藏基类的属性

时间:2012-06-29 17:44:28

标签: c# generics inheritance base-class

我正在努力实现的是两个类,非泛型和通用。

我需要一个非泛型类,因为我打算插入此类的对象并将它们插入List<T>

这是代码

// Non-generic
public class Response
{
    public object Value { get; }
}

// Generic
public class Response<T> : Response
{
    public T Value { get; set; }
}

我想要一个List<Response>,当我访问这个对象时,我将Value属性作为对象。

但是当我将此对象作为泛型接收时,访问T Value属性并隐藏对象Value属性。

我希望明确,如果没有..请告诉我。

编辑:这是一个测验。所以,每个问题都有答案。例如在MultipleChoiceQuestion中,它可以有几个答案A,B,C,D作为视图形状,或者可以是字符串或整数。

public abstract class Question
{
    public Question(string questionText)
    {
        this.QuestionText = questionText;
    }

    public string QuestionText { get; set; }
}

// Non-generic
public class Response
{
    public object Value { get; }
}

// Generic
public class Response<T> : Response
{
    public T Value { get; set; }
}

public class MathProblemQuestion : Question
{
    public Response Response { get; set; }
}

public class MultipleChoiseQuestion : Question
{
    public Response Response { get; set; }

    public IEnumerable<Response> PossibleResponses;

    ...
}

public class TrueOrFalse : Question
{
    ...
}

6 个答案:

答案 0 :(得分:10)

我个人只是给他们不同的名字。它将使您的生活更多更简单,代码更清晰:

public abstract class Response
{
    public abstract object ObjectValue { get; }
}

public class Response<T> : Response
{
    public T Value { get; set; }

    public override object ObjectValue { get { return Value; } }
}

请注意,我已经将Response类抽象化了 - 否则很难看出它如何优雅地工作;如果Response拥有自己的Value存储空间,那么可能会将其设置为非T值。希望这个摘要对你来说不是问题。

答案 1 :(得分:2)

  

我需要一个非泛型类,因为我打算插入此类的对象并将它们插入List<T>

不确定我明白这一点。 List<T>当然可以包含泛型类型。

var ls = new List<List<int>>;  // works!

如果你再回到使用object,那么使用泛型有什么意义呢?您不再拥有强类型集合用于所有意图和目的,因为基础数据(您真正关心的数据)属于object类型。

  

但是当我将此对象作为泛型接收时,访问T Value属性并隐藏对象Value属性。

如果您使用Response<object>,那么T object适用于您所有类型的实例。

也许我在这里遗漏了一些东西。如果我能为你详细说明吗?我认为你的通用类型没有理由不起作用。

编辑:我想我现在明白了。您希望单个List<T>保存具有不同通用参数的Response个对象。如果可能,我建议限制每个泛型类型以实现特定的接口,即

public interface IResponseData
{
    // whatever
}

public class Response<T> where T : IResponseData
{
    public T Value { get; set; }
}

现在,如果您需要允许int和其他没有共同祖先的类型,那么这将无效,Jon Skeet为您提供了更好的解决方案。

答案 2 :(得分:1)

为了做你想做的事,你需要将两个值联系在一起:

public abstract class Response
{
    public object Value { get; set; }
}

public class Response<T> : Response
{
    private T _value;
    public new T Value
    {
        get { return _value; }
        set { base.Value = _value = value; }
    }
}

然而,正如其他人指出的那样 - 你的前提是不正确的:你可以使用泛型类型作为其他泛型类型的类型参数:

List<Response<YourObject>> 

完全有效。

答案 3 :(得分:0)

如果您执行以下操作(使用“new”关键字),您将隐藏基类上的属性:

// Non-generic
public class Foo
{
    public object Value { get; set; }
}

// Generic
public class Foo<T> : Foo
{
    public new T Value { get; set; }
}

除非您添加另一个具有不同名称的属性,否则我找不到解决方法。我不完全理解为什么你需要拥有不同版本的类才能使用列表,你可以拥有List&lt; Foo&lt; T> &GT;很好。

答案 4 :(得分:0)

我假设您要在同一列表中插入不同类型的响应,并使Response的对象版本不可设置。但是,下面的代码不起作用,因为属性的自动生成的后备存储不是捆绑在一起的:

// Non-generic
public class Response
{
    public object Value { get; private set; }
}
// Generic
public class Response<T> : Response
{
    public new T Value { get; set; }
}

public static void Main()
{
    List<Response> List = new List<Response>() { new Response<int>() { Value = 1 }, new Response<string>() { Value = "p" } };

    // Throws NullReferenceException
    Console.WriteLine(list[0].Value);
    Console.WriteLine(list[1].Value);
}

试试这个,它使用受保护的字段来支持属性:

// Non-generic
public class Response
{
    protected object valueObject;

    public object Value
    {
        get
        {
            return valueObject;
        }
    }
}
// Generic
public class Response<T> : Response
{
    public new T Value
    {
        get
        {
            return (T)valueObject;
        }
        set
        {
            valueObject = value;
        }
    }
}

public static void RunSnippet()
{
    List<Response> list = new List<Response>() { new Response<int>() { Value = 1 }, new Response<string>() { Value = "p" } };

    Console.WriteLine(list[0].Value);
    Console.WriteLine(list[1].Value);
}

答案 5 :(得分:0)

如果我理解你的问题,你可以做你想要的,只要你愿意在阅读时手动对类别元素进行类型转换。

你问题的第一部分是,你如何用一个不同类型的新属性“隐藏”一个继承的属性,你已经实现了这一点。您不需要任何特殊关键字来替换属性。鉴于您的样本类型:

public class Response
{
    public object Value
    {
        get;
        set;
    }
}

public class Response<T> : Response
{
    public T Value
    {
        get;
        set;
    }

}

您已经可以执行以下操作:

// typeof(response.Value) == System.Object
var response = new Response();

// typeof(responseint.Value) == System.Int32
var responseint = new Response<int>();

然而,你要进一步要求做的是,将它们放入List<Response>并仍将它们视为派生类型,如果没有明确的类型转换,将无法工作。 List<>的元素类型在编译时是固定的,并且就编译器所知,列表的所有元素都具有该类型。如果需要,可以向列表中添加更多派生类型,但是一旦它们在那里,当你得到退出时,编译器只知道它们是基类型。

在运行时,当然,存在实际的类型元数据,因此运行时知道如何将Response转换为Response<int> ,只要这就是它的真实含义。为了在代码中进行演示,给出上述ResponseResponse<int>的实例:

 // this part is fine, but...
var responses = new List<Response>();
responses.Add(response);
responses.Add(responseint); 

// This will not work.
responses[1].Value += 1; 

// But this will.
(responses[1] as Response<int>).Value += 1;