C# 类类型的参数字符串+;身份证件

C# 类类型的参数字符串+;身份证件,c#,asp.net-mvc,oop,C#,Asp.net Mvc,Oop,此处的简化报告示例: 假设我有三个不同的值对象,它们都具有ID属性: public class ObjectA() { int ID { get; set; } } public class ObjectB() { int ID { get; set; } } public class ObjectC() { int ID { get; set; } } 假设我想使用URL中的“ClassTypePlusID”参数,如下所示: http://www.example.com/show/A1 ht

此处的简化报告示例:

假设我有三个不同的值对象,它们都具有ID属性:

public class ObjectA() { int ID { get; set; } }
public class ObjectB() { int ID { get; set; } }
public class ObjectC() { int ID { get; set; } }
假设我想使用URL中的“ClassTypePlusID”参数,如下所示:

http://www.example.com/show/A1
http://www.example.com/show/B3
http://www.example.com/show/C2
现在,在我的视图模型中,提取类型(A、B或C)和ID(1、3或2)非常容易。因此,创建一个值对象并不困难(简化,无错误处理):

然后,我有三个GetData()方法来获取与对象相关的数据(这里再次大大简化):

最后,我们找到了我痛苦的根源。。。现在我需要将该值对象提供给一个方法。但我必须(重新)将对象转换为正确的类型,然后才能将其用作参数。我不能这样做:

var valueObject = GetValueObject(urlParameter);
GetData(valueObject);
public class ObjectA() : IObject
编译器(我想这是正确的)无法从object转换为ObjectA、ObjectB或ObjectC。所以我不得不做这个可笑的扭曲动作:

    var valueObject = GetValueObject("A1");
    if (valueObject is ObjectA)
    {
        GetData((ValueObjectA)valueObject);
    }
    else if (valueObject is ObjectB)
    {
        GetData((ValueObjectB)valueObject);
    }
    else if (valueObject is ObjectC)
    {
        GetData((ValueObjectC)valueObject);
    }
雪上加霜的是,我还没有完成if语句,因为我必须在视图中再次使用它们才能显示数据对象(无论是哪一个)

因此,我一直在追根究底,试图弄清楚如何在没有所有丑陋(难以维护)的if语句的情况下做到这一点。必须有一种面向对象的方法来做到这一点,但我正在努力弄清楚它是什么

EDIT:OK,如我所想,第一个建议是使用继承。让我试试,也许你能告诉我我错过了什么。

我确实了解继承和多态性的基础知识,但我仍在学习如何有效地使用它们(如您所见)

因此,如果我要使用继承,我需要有一个抽象类,该类在要进行继承的对象之间具有公共属性/行为。我看不出有任何共同之处。我在示例中展示的
GetData
方法将利用特定于对象的存储库,并在ViewModel中返回(或存储)DTO,以便视图可以访问它。因此,该方法的输入不常见(它将是三个值对象之一),而该方法的输出也不常见(它将是包含相应表中数据的三个不同DTO之一)

我不是说继承或多态性不是答案(我肯定是的),我只是不知道如何利用它

编辑#2:在我试图简化这个问题时,我可能走得太远了。为移动的目标道歉,第一次没有正确表达我的问题,这不是我的意图

在最初的问题中,我试图推断ObjectA、B和C是实际值对象(每个DDD),但我从来没有明确地说出来。所以我现在说,我更新了示例代码,希望能更好地加强这一点

GetData
方法移动到Value对象中会出现一些问题。首先,我认为它不属于那里。ValueObject的用途是描述特定类型的ID。它在我的域中的多个实体中使用。另一个问题是,在我的示例中,
GetData
方法被简化了(可能太多了),它实际上有附加的ID参数(也从URL中提取)。从这里开始,
GetData
方法将利用各种存储库(来自多个数据集)用所需的数据填充视图模型。所以这里的底线是我在这里的应用服务层。我不相信
GetData
方法属于域层中的值对象内部


希望这能更好地澄清这个问题(但我觉得我肯定可以做得更好,如果我有什么想法,我会继续思考并更新)。

为什么你不能有一个界面,你可以坚持,然后有一个单一的功能,并删除所有的分支。。。此外,int.parse和子字符串都是肤浅的,分配这样小的短期堆对象是很糟糕的做法。。。您只需要Id为字符串[0]和字符串[1]的字符。

要扩展Jay的答案,以下是如何实现:

该接口理想地定义了其他不相关类之间的所有通用方法和属性。您已经实现了此接口,因此只需添加如下接口:

var valueObject = GetValueObject(urlParameter);
GetData(valueObject);
public class ObjectA() : IObject
为了使这个接口真正有用,我建议将
GetData
方法移动到它们的相关类中。这样,您就可以修改界面以包含该方法:

public interface IObject
{
    int ID { get; set; }
    void GetData();
}
完成此操作后,您可以更改
getValueObject
方法的签名以返回接口:

public IObject GetValueObject(string ClassTypePlusID)
然后,不必检查对象的类型,只需调用其GetData方法:

var valueObject = GetValueObject("A1"); //var is of type IObject
ObjectA.GetData();

下面的方法行吗

我不清楚GetData()方法应该是由对象完成的还是在实例化它们的类上完成的。如果要避免If-else块,则需要预先知道它们的共同属性,以便声明适当的接口,或者将获取数据的责任委托给对象本身(正如我在下面通过实现IDataProvider接口所做的那样)

我使用了一个字典在各种字符串和类型本身之间映射(只做了a和B,但您可以扩展它)。我已经使用泛型来解决您的类型转换问题,但是您甚至可能不需要知道类型,因为对于调用方类来说,重要的是它可以获取数据(),而不是类是什么。我已经在c的测试用例中展示了这一点,它可以同样容纳ObjectB的一个实例

public interface IDataProvider
{
    void GetData();
}
public class ObjectA : IDataProvider
{
    public ObjectA(int id)
    {
        this.Id = id;
    }

    public int Id
    {
        get;
    }

    public void GetData()
    {
        // Get A's data
    }
}
public class ObjectB : IDataProvider
{
    public ObjectB(int id)
    {
        this.Id = id;
    }

    public int Id
    {
        get;
    }

    public void GetData()
    {
        // Get B's data
    }
}

public static class ObjectFactory
{
    private static readonly Dictionary<string, Type> typeByNameDictionary = new Dictionary<string, Type>();

    static ObjectFactory()
    {
        typeByNameDictionary.Add("A", typeof(ObjectA));
        typeByNameDictionary.Add("B", typeof(ObjectB));            
    }

    public static bool TryGetObject<T>(string classTypePlusId, out T createdObject) where T : class 
    {
        string objectName = classTypePlusId.Substring(0, 1);
        if (!int.TryParse(classTypePlusId.Substring(1, 1), out int id))
        {
            throw new ArgumentOutOfRangeException(classTypePlusId, "something meaningful");
        }

        if (!typeByNameDictionary.TryGetValue(objectName, out Type objectType))
        {
            createdObject = default(T);
            return false;
        }
        createdObject = Activator.CreateInstance(objectType, id) as T;
        return createdObject != null;
    }
}

[TestFixture]
public class Test
{
    [Test]
    public void CanCreateObjects()
    {
        ObjectA a;
        ObjectB b;
        IDataProvider c;

        Assert.That(ObjectFactory.TryGetObject("A1", out a), Is.True);
        Assert.That(a.Id, Is.EqualTo(1));
        a.GetData();

        Assert.That(ObjectFactory.TryGetObject("A1", out b), Is.False);
        Assert.That(ObjectFactory.TryGetObject("B4", out b), Is.True);
        Assert.That(b.Id, Is.EqualTo(4));
        b.GetData();            
        Assert.That(ObjectFactory.TryGetObject("A2", out c), Is.True);
        c.GetData();
    }
}
然后可以声明ObjectA、ObjectB和ObjectC以支持这些。这样,服务的GetData方法只接受IObjectWithCommonProperties参数。(请挑一个
public interface IDataProvider
{
    void GetData();
}
public class ObjectA : IDataProvider
{
    public ObjectA(int id)
    {
        this.Id = id;
    }

    public int Id
    {
        get;
    }

    public void GetData()
    {
        // Get A's data
    }
}
public class ObjectB : IDataProvider
{
    public ObjectB(int id)
    {
        this.Id = id;
    }

    public int Id
    {
        get;
    }

    public void GetData()
    {
        // Get B's data
    }
}

public static class ObjectFactory
{
    private static readonly Dictionary<string, Type> typeByNameDictionary = new Dictionary<string, Type>();

    static ObjectFactory()
    {
        typeByNameDictionary.Add("A", typeof(ObjectA));
        typeByNameDictionary.Add("B", typeof(ObjectB));            
    }

    public static bool TryGetObject<T>(string classTypePlusId, out T createdObject) where T : class 
    {
        string objectName = classTypePlusId.Substring(0, 1);
        if (!int.TryParse(classTypePlusId.Substring(1, 1), out int id))
        {
            throw new ArgumentOutOfRangeException(classTypePlusId, "something meaningful");
        }

        if (!typeByNameDictionary.TryGetValue(objectName, out Type objectType))
        {
            createdObject = default(T);
            return false;
        }
        createdObject = Activator.CreateInstance(objectType, id) as T;
        return createdObject != null;
    }
}

[TestFixture]
public class Test
{
    [Test]
    public void CanCreateObjects()
    {
        ObjectA a;
        ObjectB b;
        IDataProvider c;

        Assert.That(ObjectFactory.TryGetObject("A1", out a), Is.True);
        Assert.That(a.Id, Is.EqualTo(1));
        a.GetData();

        Assert.That(ObjectFactory.TryGetObject("A1", out b), Is.False);
        Assert.That(ObjectFactory.TryGetObject("B4", out b), Is.True);
        Assert.That(b.Id, Is.EqualTo(4));
        b.GetData();            
        Assert.That(ObjectFactory.TryGetObject("A2", out c), Is.True);
        c.GetData();
    }
}
public interface IObjectWithCommonProperties
{
    int Id
    {
        get;
    }

    string StoredProcName
    {
        get;
    }

    bool IsSomeOtherFact
    {
        get;
    }

            // etc
}
public interface IObjectBuilder
{
    object GetCreateObjectWithData(string classPlusId);
}

public class ObjectABuilder : IObjectBuilder
{
    public object GetCreateObjectWithData(string classPlusId)
    {
        ObjectA loadedObject;
        ObjectFactory.TryGetObject(classPlusId, out loadedObject);

        loadedObject.SomeProperty = "someValue"; // etc
        return loadedObject;
    }
}