将通用输入对象传递给方法C#

时间:2016-04-22 06:08:17

标签: c# generics

我有一个方法ABC(),它从我的应用程序中的两个不同的地方调用。从这两个地方我都有不同的类对象,它实现了一个共同的接口“IDestination”。

我的两个类和接口看起来像这样:

public class ClassA: IDestination
{
        public string Var1 { get; set; }    
        public string Var2 { get; set; }    
        public string Var3 { get; set; }
}

public class ClassB: IDestination
{
        public string Var1 { get; set; }
        public string Var2 { get; set; }
        public string Var3 { get; set; }
        public string Var4 { get; set; }
        public string Var5 { get; set; }
}

public interface IDestination
{
        string Var1 { get; set; }
        string Var2 { get; set; }
}

截至目前方法ABC()接受classA的对象,我希望它也可以接受classB的对象。为此我已经使我的方法定义通用如下:

public string ABC<T>(T obj)
{

}

但是,问题出在ABC方法中我想要访问类的属性(classA和classB)。

public string ABC<T>(T obj)
 {
        //Some code
        obj.var2; //of classA

        obj.var4; //of classB
        //Some code
 }

我不允许在界面中进行任何更改。

我怎样才能做到这一点?我不想创建另一种处理不同类对象的方法。有什么想法吗?

7 个答案:

答案 0 :(得分:3)

首先,接口不会被继承,但已实现

另一方面,可以约束通用参数:

public string ABC<T>(T obj) where T : IDestination
OP说:

  

我无法对界面进行任何更改。

顺便说一下,根据您的要求以及通用类型限制,您无法接受classAclassB,因为C#不支持多重继承。如果您想要访问classB成员,则需要展开{​​{1}}界面,以IDestination键入classB来定义您要访问的所有媒体资源:IDestination

public interface IDestination
{
      string Var1 { get; set; }
      string Var2 { get; set; }
      string Var3 { get; set; }
      string Var4 { get; set; }
      string Var5 { get; set; } 
}

或者您可以使用其余属性定义第二个接口:

public interface IDestination2
{
      string Var3 { get; set; }
      string Var4 { get; set; }
      string Var5 { get; set; } 
}

...您将在classB上实施它:

public class ClassB: IDestination, IDestination2

无论如何,这里的问题是你不能约束泛型参数来接受某些给定类的两个不同的继承。也就是说,例如,如果您将T约束为IDestinationIDestination2,则您无法将ClassA作为参数,因为它不会#39; t实施IDestination2

OP说:

  

我不想创建另一种处理不同类对象的方法。

实际上,这也可以在没有泛型的情况下解决,但是使用方法重载,即使你不想走这条路,它也不是一种邪恶的方法:

public void ABC(IDestination destination) {}
public void ABC(IDestination2 destination) {}

// or directly...

public void ABC(ClassA destination) {}
public void ABC(ClassB destination) {}

否则,您需要按如下方式实现:

public string ABC<T>(T obj) where T : IDestination
{
     ClassA a = obj as ClassA;
     ClassB b = obj as ClassB;

     // Now if you want to access Var1, Var2 you can access them
     // using "obj" reference, because T is IDestination
     string var1 = obj.Var1;
     string var2 = obj.Var2;

     if(a != null) 
     {
         // Here access all ClassA members...
     }

     if(b != null)
     {
         // Here access all ClassB members...
     }
}

对我而言,上述方法是一个设计缺陷。如果我使用泛型类型和接口是因为我想使用与泛型类型参数的类型相等的对象。

如果我开始对界面的特定实现执行向下倾斜,就像击败使用IDestination的目的一样,看起来您的方法可能只是接受{ {1}}因为您的方法会访问objectClassA成员而不是ClassB成员:

IDestination

TL; DR

总之,您应该使用接口来键入对象和通用约束,以保证在引用上最小化输入以避免强制转换。否则,通用打字不是您的解决方案。

答案 1 :(得分:1)

你应该明确地重新思考你的设计。当您的方法接受接口的实例时,它应该适用于所有类型,而不仅仅是一个集合。想象一下,您创建了另一个也实现接口的类型。您必须重新实现整个方法来支持它。因此,应该在接口而不是类级别定义所有属性,然后可以在方法中访问它们。

但是,如果你真的必须使用当前的方法,你可以简单地将其转换为适当的类型:

ClassA a = obj as ClassA;
if (a != null) a.Var2 = ...
// this will fail if a user provides an instance of ClassC which also implements the interface
else ((ClassB)obj).Var4 = ...   

为此,您还需要对gegernic参数进行约束:

public string ABC<T>(T obj) where T : IDestination

答案 2 :(得分:0)

我认为更好的设计如下:

var input = document.getElementById("hidLastName")
   input.value =  document.getElementById("last_name").value

我之所以这么说,是因为你想要一个实现public string ABC<T>(T obj) where T : IDestination { } 接口的对象的泛型方法,所以它作为一个设计会更好,如果你已经将它声明为约束。为此,请查看here

然后,为了在IDestination中访问所需的字段,界面应具有所有这些属性。因此,如果我们假设您希望两个类ABCA具有相同的三个属性BVar1Var2,并且您希望在Var3,然后您必须重新声明您的界面,如下所示:

ABC

答案 3 :(得分:0)

我认为在泛型方法中很难处理特定于类型的属性,因为它不知道这些属性的具体内容。我建议将这个逻辑委托给一个方法,这个类将通过接口实现。

正如您在评论中提到的,您无法更改界面IDestination,并且您不希望破坏兼容性,因为此解决方案已在许多地方使用过。所以这个命题不是改变已经有效的ClassAClassB逻辑,而是添加新的接口实现,它将涵盖你想在ABC方法中实现的这种类型条件逻辑。

我会添加另一个接口来保存所有类型条件逻辑的方法,并简单地在ABC类的中间调用它。为了保持约束,您的泛型类型需要实现IDestination和IDestinationLogic接口。当然ABC方法可能需要稍作修改,但很难说它看起来有多精确,因为我们完全不了解它应该做什么。

示例实现可能如下所示:

public class GenericMethodClass
{

    public string ABC<T>(T obj) where T : IDestination, IDestinationLogic
    {
        var result = string.Empty;

        //some code happens here

        var typeConditionalLogicResult = obj.DoSomething(); 

        // do more stuff with according to the result of type-specific calculations

        return result;
    }
}


public class ClassA: IDestination, IDestinationLogic
{
    public string Var1 { get; set; }    
    public string Var2 { get; set; }    
    public string Var3 { get; set; }

    public string DoSomething()
    {
        return Var2;        
    }
}

public class ClassB: IDestination, IDestinationLogic
{
    public string Var1 { get; set; }
    public string Var2 { get; set; }
    public string Var3 { get; set; }
    public string Var4 { get; set; }
    public string Var5 { get; set; }

    public string DoSomething()
    {
        return Var4;        
    }
}

public interface IDestination
{
    string Var1 { get; set; }
    string Var2 { get; set; }   
}

public interface IDestinationLogic
{
    string DoSomething();   
}

我建议使用接口的方法与模板方法设计模式非常相似,模板方法设计模式最初包含抽象类。你可以在这里阅读:http://www.dofactory.com/net/template-method-design-pattern也许它会更加清楚你的案例

答案 4 :(得分:0)

嗯,要实现你想要的,你需要实现ABC方法来接受一种IDestination:

public string ABC<T>(T obj) where T : IDestination

但要实现这一点,您必须更改界面以获得要访问的所有方法:

    public interface IDestination
    {
        string Var1 { get; set; }
        string Var2 { get; set; }
        string Var3 { get; set; }
        string Var4 { get; set; }
        string Var5 { get; set; }
    }

然后你必须改变你的类来实现界面的所有成员,这意味着你可能会得到这样的结果:

    public class ClassA : IDestination
    {
        public string Var1 { get; set; }
        public string Var2 { get; set; }
        public string Var3 { get; set; }

        public string Var4
        {
            get
            {
                throw new NotImplementedException();
            }

            set
            {
                throw new NotImplementedException();
            }
        }

        public string Var5
        {
            get
            {
                throw new NotImplementedException();
            }

            set
            {
                throw new NotImplementedException();
            }
        }
    }

    public class ClassB : IDestination
    {
        public string Var1 { get; set; }
        public string Var2 { get; set; }
        public string Var3 { get; set; }
        public string Var4 { get; set; }
        public string Var5 { get; set; }
    }

但如果他们只抛出一个异常,那么在ClassA中实现额外的方法并没有多大意义。所以你不妨完全实现它们。但是ClassA和ClassB是完全相同的。所以你必须问问自己,每个班级的独特之处是什么才能证明IDestination的单独具体实施?回答这个问题将指导您做什么。

修改 既然你无法改变界面,你是否被迫在方法ABC中使用界面?你能创建一个ClassA和ClassB都继承的抽象类吗?

public abstract class ClassAB : IDestination //might not need the interface
{
  public virtual string Var1 { get; set; }
  public virtual string Var2 { get; set; }
  public virtual string Var3 { get; set; }
  public virtual string Var4 { get; set; }
  public virtual string Var5 { get; set; }
}

public class ClassA : ClassAB
{
   //override any of the virtual methods needed
}

public class ClassB : ClassAB
{
   //override any of the virtual methods needed
}

然后改变ABC方法:

public string ABC(ClassAB classAB)
{
  //all the methods are available on classAB
}

ClassA和ClassB仍然是IDestination类型,因为抽象父类实现它。唯一的问题是你是否可以改变方法ABC以期望一种类型的ClassAB而不是IDestination?

答案 5 :(得分:0)

您可以使用反射类来处理该问题,您可以在运行时获取参数的属性,也可以使用。

Type type = typeof(obj);
PropertyInfo[] propertyList = type.GetProperties();

答案 6 :(得分:0)

您可以使用Reflection获取这样的值:

public static string ABC<T>(T obj) 
    {
        string s=string.Empty;
        //Some code
        if(obj is ClassA)
            s = obj.GetType().GetProperty("Var2").GetValue(obj, null).ToString(); //of classA

        if (obj is ClassB)
        s = obj.GetType().GetProperty("Var4").GetValue(obj,null).ToString(); //of classB
        //Some code
        return s;
    }