如何在泛型类中访问泛型成员的属性

4
我是一名C#新手,正在尝试创建一个通用类。我有三个类和一个主/通用类。
三个类:
public class A
{
    public string Name { get; set; }
    public string Address { get; set; }

    public A(string _name, string _address)
    {
        Name = _name;
        Address = _address;
    }        
}

public class B
{
    public string Name { get; set; }
    public string Address { get; set; }

    public B(string _name, string _address)
    {
        Name = _name;
        Address = _address;
    }
}

public class C
{
    public string Name { get; set; }
    public string Address { get; set; }

    public C(string _name, string _address)
    {
        Name = _name;
        Address = _address;
    }
}

泛型类

public class GenericClass<T>
{
    public GenericClass(T obj)
    {
        DynamicObject = obj;
    }

    public T DynamicObject { get; set; }

}

我已成功创建了一个通用类。

class Program
{
    static void Main(string[] args)
    {
        A objA = new A("Mohit", "India");
        GenericClass<A> objGenericClass = new GenericClass<A>(objA);

        Console.ReadLine();
    }

}

现在,如果我需要在泛型类中使用A/B/C类属性,该怎么办?我知道类引用类型在运行时决定。因此,我不能以以下方式使用它。但是,还有其他方法吗?

public class GenericClass<T>
{
    public GenericClass(T obj)
    {
        DynamicObject = obj;
    }

    public T DynamicObject { get; set; }


    public void UseClassPro()
    {
        Console.WriteLine("Address " + DynamicObject.Address);//Compile time error here.
    }

}
5个回答

6

其他回答是正确的,但是......

我只想指出:虽然其他答案提供了有效的C#代码,但它们使你实现的泛型方面变得多余。你不再需要使用泛型:

假设有一个基类或接口,如下所示:

public interface IHasAddress
{
    string Address { get; }
}

您不再需要使用通用类来实现您想要的功能(根据您提供的代码,我可以确定这一点):
public class NotSoGenericClass
{
    public GenericClass(IHasAddress obj)
    {
        DynamicObject = obj;
    }

    public IHasAddress DynamicObject { get; set; }    
}

因此,您可以看到,您可以轻松实现所需的行为而不使用泛型。
对于初学者,我建议在使用泛型时遵循以下基本规则:
1. 当您认为必须使用泛型时,请先通过接口、抽象类或基类考虑抽象化。这通常会导致更简单和更清洁的解决方案。
2. 反射也是如此。当您认为需要反射时,请考虑使用泛型(此时仍适用规则1)。
但是,并非泛型有问题,它只是更复杂,通常不是必需的。请将上面的类与泛型解决方案进行比较:
public class GenericClass<T> where T : IHasAddress  // just for the sake of generics
{
    public GenericClass(T obj)
    {
        DynamicObject = obj;
    }

    public T DynamicObject { get; set; }    
}

看起来更加复杂,而且并没有增加任何好处,是吗?请注意,无论如何都需要一个接口/基类。否则,您也可以使用反射(不建议)。

实际回答您的问题

对于您的问题,精确的答案是:

您必须定义您的泛型参数可以分配给IHasAddress,使用

public class GenericClass<T> where T : IHasAddress
                             ^^^^^^^^^^^^^^^^^^^^^
                                  This Part

这样,编译器就知道 T 继承或属于类型 IHasAddress 或任何你定义的类型。你也可以在此处传递多个类型,这样在设计接口时就更加灵活。或许,在你的用例中有一些需要考虑的点,这些点可能从你提供的问题信息中不太明显。如果是这样,请随时添加一些细节,我很乐意深入探讨这些问题。

同意,使用泛型类不应该关心所包含的类的实现细节。 - Janne Matikainen
嗯,这取决于情况 ;) - nozzleman
如果您在设计中添加了越来越多的接口,那么“通用”类应该知道如何利用这些接口,那么这个类就会变得越来越不通用。使用扩展来扩展通用功能将是更好的方法。 - Janne Matikainen
@nozzleman 谢谢。 - KiddoDeveloper
@Mohit 很高兴我能帮到你 ;) - nozzleman

5

定义接口:

public interface IABC
{
    string Name { get; set; }
    string Address { get; set; }
}

并且在您的通用类定义中指定此接口:

public class GenericClass<T> where T: IABC
{
    public GenericClass(T obj)
    {
        DynamicObject = obj;
    }

    public IABC DynamicObject { get; set; }    
}

你的所有三个类都实现了这个接口:

public class A : IABC
public class B : IABC
public class C : IABC

接下来,您可以调用IABC的属性。

A objA = new A("Mohit", "India");
GenericClass<A> objGenericClass = new GenericClass<A>(objA);
var adress = objGenericClass.DynamicObject.Address;

1
虽然这是有效的代码,但我强烈不建议这样实现,因为在这种情况下通用类是完全无用的。 - nozzleman
@Maksim 谢谢。 - KiddoDeveloper

1
如果您的泛型参数中存在共享类型和名称的属性,请使用基类:
public class Base
{
    public string Address { get; set; }

    public A(string _address)
    {
        Address = _address;
    }        
}

public class A : Base
{
    public string Name { get; set; }

    public A(string _name, string _address) : base(_address)
    {
        Name = _name;
    }        
}

public class B : Base
{
    public string Name { get; set; }

    public B(string _name, string _address) : base(_address)
    {
        Name = _name;
    }        
}

public class C : Base
{
    public string Name { get; set; }

    public C(string _name, string _address) : base(_address)
    {
        Name = _name;
    }        
}

你可以将基类用作泛型类的约束条件:
public class GenericClass<T> where T : Base
{
    public GenericClass(T obj)
    {
        DynamicObject = obj;
    }

    public T DynamicObject { get; set; }


    public void UseClassPro()
    {
        Console.WriteLine("Address " + DynamicObject.Address);//Compiles now
    } 
}

如果您无法使用基类,请使用接口:
public interface IBase
{
    string Address { get; set; }
}

public class A : IBase
{
    public string Name { get; set; }
    public string Address { get; set; }

    public A(string _name, string _address)
    {
        Name = _name;
        Address = _address;
    }        
}

public class B : IBase
{
    public string Name { get; set; }
    public string Address { get; set; }

    public B(string _name, string _address)
    {
        Name = _name;
        Address = _address;
    }
}

public class C : IBase
{
    public string Name { get; set; }
    public string Address { get; set; }

    public C(string _name, string _address)
    {
        Name = _name;
        Address = _address;
    }
}

public class GenericClass<T> where T : IBase
{
    public GenericClass(T obj)
    {
        DynamicObject = obj;
    }

    public T DynamicObject { get; set; }


    public void UseClassPro()
    {
        Console.WriteLine("Address " + DynamicObject.Address);//Compiles now
    }
}

0

添加一个接口,实现类似以下属性:

public interface IInfo
{
    public string Name { get; set; }
    public string Address { get; set; }
}

public class GenericClass<T> where T : IInfo
{
    public GenericClass(T obj)
    {
        DynamicObject = obj;
    }

    public T DynamicObject { get; set; }


    public void UseClassPro()
    {
        Console.WriteLine("Address " + DynamicObject.Address);
    }

}

0

如果没有继承,无论是通过接口还是基类,都没有一种“干净”的方法从泛型类中获取属性,正如其他答案所示。

如果您不想使用继承,则可以使用反射,但这比使用继承类要低效得多。

// the generic class
public class GenericClass<T> where T: IABC
{
    public GenericClass(T obj)
    {
        DynamicObject = obj;
    }
    public IABC DynamicObject { get; set; }    

    public T GetPropertyValue<T>(string propertyName)
    {
        var obj = GetType().GetProperty(propertyName).GetValue(this);
        return (T)Convert.ChangeType(obj, typeof(T))
    }
}

A objA = new A("Mohit", "India");
GenericClass<A> objGenericClass = new GenericClass<A>(objA);
var address = objGenericClass.GetPropertyValue<string>("address");

我强调这是一种替代继承的方法,虽然速度可能不太快,但可能适合您的需求。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接