【问题标题】:"Writable" reference to object对对象的“可写”引用
【发布时间】:2014-01-21 13:19:19
【问题描述】:

不确定我能否以人们容易理解的方式提出这个问题,所以让我们举一个 cool 营销示例:

public class Part
{
    public MemberType member;
    ...
}

public class Product
{
    public Part part1;
    ...
}
...
Product product = new Product();

我需要修改公共productpart1。所以,自然的方法是写这样的:

product.part1 = new Part();

现在,一种算法(假设是一种搜索算法)将遍历 product 对象并将 part1 识别为有趣的部分并返回对它的引用:

Part Search(Product product)
{
    Part part = null;
    ...
    part = product.part1;
    ...
    return part;
}
...
interesting_part = Search(product);

我们可以通过interesting_part 来改变product 对象

interesting_part.member = whatever;

现在,问题:在 c/c++ 中,如果 Product.part1 是指向 Part 的指针并且 Search 返回此指针的地址,我们可以通过分配新值来替换 part1到这个地址。 AFAIK 这对于 c# 参考是不可能的:

interesting_part = new Part();

只是创建新对象并将其引用复制到interresting_part,但在不知道成员父(product 对象)的情况下,我们无法修改(product.part1)引用,只能修改其内容。我们需要二级参考。

是否有类似“ref reference”类型的东西可以接受引用地址?在这种假设的情况下,搜索将返回 ref Part 并分配给这样的值会用新的对象替换引用的对象.

谢谢。

【问题讨论】:

  • 你的Search返回一个引用,而不是一个“对象”本身。
  • 您可以将引用包装在一个类中并传递该包装器的实例。
  • 保留对父类的引用。
  • @Eli 谢谢,有用的链接。

标签: c# pointers reference


【解决方案1】:

你可以创建一个Reference

class Reference<T>
{
  private Func<T> m_Getter;  
  private Action<T> m_Setter;


  public Reference(Func<T> getter, Action<T> setter)
  {
    m_Getter = getter;
    m_Setter = setter;
  }

  public T Value
  {
    get{return m_Getter();}
    set{m_Setter(value);}
  }
}

现在你可以说

Reference<Part> Search(Product product)
{
  Part part = null;
  ...
  part = product.part1;

  var reference=new Reference<Part>(()=>product.part, (value)=>product.part1=value);

  return refernce;
}

var partReference = Search(product);
partReference.Value = someNewPart;

【讨论】:

  • 我做了类似的事情并将其称为访问器,但对我来说似乎过于复杂......很高兴看到它不是那么离题。
【解决方案2】:

在非常相似的情况下,我在每个子对象中保留了父对象的引用。简单而有效。

public class Part
{
    public MemberType member;
    ...

    public Product parent;

    Part(Product p)
    {
         parent = p;
    }
}

public class Product
{
    public Part part1;
    ...
}

【讨论】:

  • 是的,这似乎是一个合适的解决方法,但对于复杂的结构和不同的数据类型,父引用会更加复杂,单变量可变引用将是优雅的解决方案......谢谢无论如何。
【解决方案3】:

我认为你做不到。您需要改变对您product 对象的引用,或者添加其他一些引用层。

【讨论】:

  • 好的,显然没有这样的语言结构 :o(
【解决方案4】:

所以你需要建立一个代理对象。产品将获得对代理的引用,并且可以交换(隐藏的)部分。这是一种常见的 OO 设计模式。当然Proxy可以将方法调用委托给Part。

【讨论】:

    【解决方案5】:

    如果你想更改field,你可以这样做,

    class Program
    {
        static void Main(string[] args)
        {
            var holder = new Holder();
            holder.CurrentPart = new Part() { Name = "Inital Part" };
            Console.WriteLine(holder.CurrentPart.Name);
            TestRef(ref holder.CurrentPart);
            Console.WriteLine(holder.CurrentPart.Name);
            Console.ReadKey();
        }
    
        public static void TestRef(ref Part part)
        {
            part = new Part() { Name = "changed" };
        }
    }
    
    public class Part
    {
        public string Name;
    }
    
    public class Holder
    {
        public Part CurrentPart;
    }
    

    这不适用于属性、索引器等。

    【讨论】:

    • 这也是我的第一个想法,但我怀疑 OP 正在寻找方法中的 return 引用,然后使用它(从他如何陈述他的问题)。根据我对 C# 的了解,无法在返回值上使用 ref 关键字。
    • 你的怀疑似乎是对的,我的错。我想我会尽可能地把它作为一个例子。
    【解决方案6】:

    据我所知,没有任何方法可以在不引用对象的情况下更改对象的“父对象”。因此,我相信您所写问题的官方回答是“否”。

    也就是说,有很多方法可以完成所写的任务。最简单的选择是从零件对象添加对父对象的引用。你最终会得到类似的东西:

    public class Part
    {
        public Product parentProduct;
        public MemberType member;
        ...
    }
    

    现在,只要您有一个零件对象,您也知道该零件搭配什么产品(如果它确实与零件搭配)。这不一定是一种糟糕的编码风格,但肯定存在缺陷。您可以更新产品,但忘记更新该产品中的部件,您正在编码以使部件具有一种产品,但如果该部件具有许多产品怎么办?您可以看到这是如何工作的,但它可能会变得复杂。

    采用它并使其更通用,您可以将父级引用为对象类型。看起来像:

    public class Part
    {
        public object parent;
        public MemberType member;
        ...
    }
    

    现在,当您想使用父级时,您可以编写如下内容:

    var parentProduct = myPart.parent as Product;
    

    这会将父级转换为产品,如果父级不是产品类型,则将分配 null。现在,部件可以拥有任何您想要的给定类型的父级,并且您使模式更加灵活。

    我知道人们经常使用的最后一种模式是委托。这允许您传入一个函数,从而有效地修改“搜索”的工作方式。假设您真正想做的是搜索,然后以某种方式处理结果,但您希望该处理灵活(这可能是您对结果所做的事情)。在这种情况下,您可以按如下方式使用委托:

        // define the delegate
        public delegate void ProcessResultDelegate(Product result, Part interestingPart);
    
        // an example search function
        public static void RunSearch(IEnumerable<Product> products, ProcessResultDelegate processingHelper)
        {
            // run the search... then call the processing function
            processingHelper(searchResult, interestingPart);
        }
    

    当您想要修改例程的行为而不是该例程的返回值时,此模式更有用。

    无论如何,希望这些模式能有所帮助!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-09-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-27
      • 1970-01-01
      • 1970-01-01
      • 2021-04-12
      相关资源
      最近更新 更多